#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#  MA 02110-1301, USA.
#

from __future__ import division
import pcbnew

import FootprintWizardBase
import PadArray as PA


class RowedGridArray(PA.PadGridArray):

    def __init__(self, *args, **kwargs):
        super(RowedGridArray, self).__init__(*args, **kwargs)

    def NamingFunction(self, x, y):
        pad_cnt = self.nx*self.ny

        if self.ny == 1:
            return x+1

        if (y % 2) == 0:  # upper row, count down
            return pad_cnt-x
        else:  # lower row, count up
            return x+1


class RowedFootprint(FootprintWizardBase.FootprintWizard):

    pad_count_key = 'pad count'
    row_count_key = 'row count'
    row_spacing_key = 'row spacing'
    pad_length_key = 'pad length'
    pad_width_key = 'pad width'
    pad_pitch_key = 'pad pitch'

    silkscreen_inside_key = 'silk screen inside'
    outline_x_margin_key = 'outline x margin'
    outline_y_margin_key = 'outline y margin'

    def GenerateParameterList(self):
        # defaults for a DIP package
        self.AddParam("Pads", self.pad_count_key, self.uInteger, 24)
        self.AddParam("Pads", self.row_count_key, self.uInteger, 2, min_value=1, max_value=2)

        self.AddParam("Body", self.silkscreen_inside_key, self.uBool, False)
        self.AddParam("Body", self.outline_x_margin_key, self.uMM, 0.5)
        self.AddParam("Body", self.outline_y_margin_key, self.uMM, 0.5)

    def CheckParameters(self):
        self.CheckParam("Pads", self.pad_count_key, multiple=self.parameters['Pads'][self.row_count_key], info='Pads must be multiple of row count')

    def BuildThisFootprint(self):
        pads = self.parameters["Pads"]
        body = self.parameters["Body"]
        num_pads = pads[self.pad_count_key]
        pad_length = pads[self.pad_length_key]
        pad_width = pads[self.pad_width_key]
        row_pitch = pads[self.row_spacing_key]
        pad_pitch = pads[self.pad_pitch_key]
        num_rows = pads[self.row_count_key]

        pads_per_row = num_pads // num_rows

        # add in the pads
        pad = self.GetPad()

        array = RowedGridArray(pad, pads_per_row, num_rows, pad_pitch, row_pitch)
        array.AddPadsToModule(self.draw)

        # draw the Silk Screen
        Hsize = pad_pitch * (num_pads / num_rows - 1)
        Vsize = row_pitch * (num_rows - 1)
        pin1_posY = -Vsize / 2
        pin1_posX = -Hsize / 2

        pad_length = pads[self.pad_length_key]
        pad_width = pads[self.pad_width_key]

        ssx_offset = -pad_width / 2 - body[self.outline_x_margin_key]
        ssy_offset = -pad_length / 2 - body[self.outline_y_margin_key]

        if body[self.silkscreen_inside_key]:
            ssy_offset *= -1

        ssx = -pin1_posX - ssx_offset
        ssy = -pin1_posY - ssy_offset

        cmargin = self.draw.GetLineThickness()
        self.draw.SetLineThickness( pcbnew.FromMM( 0.12 ) ) #Default per KLC F5.1 as of 12/2018
        self.DrawBox(ssx, ssy)

        # Courtyard
        self.draw.SetLayer(pcbnew.F_CrtYd)
        cclearance = pcbnew.FromMM(0.25)
        sizex = (-pin1_posX + cclearance) * 2 + pad_width
        sizey = (-pin1_posY + cclearance) * 2 + pad_length
        # round size to nearest 0.02mm, rectangle will thus land on a 0.01mm grid
        sizex = pcbnew.PutOnGridMM(sizex, 0.02)
        sizey = pcbnew.PutOnGridMM(sizey, 0.02)
        # set courtyard line thickness to the one defined in KLC
        self.draw.SetLineThickness(pcbnew.FromMM(0.05))
        self.draw.Box(0, 0, sizex, sizey)
        # restore line thickness to previous value
        self.draw.SetLineThickness(pcbnew.FromMM(cmargin))

        #reference and value
        text_size = self.GetTextSize()  # IPC nominal

        if num_rows == 1:
            text_py = ssy + text_size
            self.draw.Value(0, -text_py, text_size)
            self.draw.Reference(0, text_py, text_size)
        else:
            text_px = ssx + text_size
            # self.draw.Value(text_px, 0, text_size, orientation_degree=90)
            self.draw.Value(0, 0, text_size)
            self.draw.Reference(-text_px, 0, text_size, orientation_degree=90)

        # set the attribute
        if self.GetName() == "S-DIP":
            self.module.SetAttributes(pcbnew.MOD_DEFAULT)
        elif self.GetName() == "SOIC":
            self.module.SetAttributes(pcbnew.MOD_CMS)

class SDIPWizard(RowedFootprint):

    def GetName(self):
        return "S-DIP"

    def GetDescription(self):
        return "Single/Dual Inline Package Footprint Wizard"

    def GenerateParameterList(self):
        RowedFootprint.GenerateParameterList(self)

        self.AddParam("Pads", self.pad_pitch_key, self.uMM, 2.54)
        self.AddParam("Pads", self.pad_width_key, self.uMM, 1.2)
        self.AddParam("Pads", self.pad_length_key, self.uMM, 2)
        self.AddParam("Pads", self.row_spacing_key, self.uMM, 7.52)
        self.AddParam("Pads", "drill size", self.uMM, 0.8)

    def GetValue(self):
        pads = self.parameters["Pads"]
        rows = pads[self.row_count_key]
        pad_count = pads[self.pad_count_key]
        row_dist_mil = pcbnew.Iu2Mils(int(self.parameters["Pads"][self.row_spacing_key])) #int(self.parameters["Pads"][self.row_spacing_key] / 2.54 * 100)
        pad_shape = ""

        if pads[self.pad_width_key] != pads[self.pad_length_key]:
            pad_shape = '_ELL'

        if rows == 1:
            name = "SIP"
            return "%s-%d" % (name, pad_count)

        name = "DIP"
        return "%s-%d_%d%s" % (name, pad_count, row_dist_mil, pad_shape)

    def GetPad(self):
        pad_length = self.parameters["Pads"][self.pad_length_key]
        pad_width = self.parameters["Pads"][self.pad_width_key]
        drill = self.parameters["Pads"]["drill size"]
        shape = pcbnew.PAD_SHAPE_CIRCLE

        if pad_length != pad_width:
            shape = pcbnew.PAD_SHAPE_OVAL

        return PA.PadMaker(self.module).THPad(
            pad_length, pad_width, drill, shape=shape)

    def DrawBox(self, ssx, ssy):

        if self.parameters["Pads"][self.row_count_key] == 2:

            #  ----------
            #  |8 7 6 5 |
            #  >        |
            #  |1 2 3 4 |
            #  ----------

            # draw the notch
            notchWidth = ssy/1.5
            notchHeight = self.draw.GetLineThickness()*3

            # NotchedBox draws the notch on top. Rotate the box 90 degrees
            # to have it on the left
            self.draw.NotchedBox(0, 0, ssy*2, ssx*2, notchWidth, notchHeight, -90)
        else:
            #  -----------------
            #  |1|2 3 4 5 6 7 8|
            #  -----------------
            self.draw.Box(0, 0, ssx*2, ssy*2)

            #line between pin1 and pin2
            pad_pitch = self.parameters["Pads"][self.pad_pitch_key]
            pad_cnt = self.parameters["Pads"][self.pad_count_key]
            line_x = ( pad_cnt/2 - 1) * pad_pitch
            self.draw.VLine(-line_x, -ssy, ssy * 2)

        return ssx, ssy

SDIPWizard().register()


class SOICWizard(RowedFootprint):

    def GetName(self):
        return "SOIC"

    def GetDescription(self):
        return "SOIC, MSOP, SSOP, TSSOP, etc, footprint wizard"

    def GetValue(self):
        pad_count = self.parameters["Pads"][self.pad_count_key]
        return "%s-%d" % ("SOIC", pad_count)

    def GenerateParameterList(self):
        RowedFootprint.GenerateParameterList(self)

        #and override some of them
        self.AddParam("Pads", self.pad_pitch_key, self.uMM, 1.27)
        self.AddParam("Pads", self.pad_width_key, self.uMM, 0.6)
        self.AddParam("Pads", self.pad_length_key, self.uMM, 2.2)
        self.AddParam("Pads", self.row_spacing_key, self.uMM, 5.2)

    def GetPad(self):
        pad_length = self.parameters["Pads"][self.pad_length_key]
        pad_width = self.parameters["Pads"][self.pad_width_key]
        return PA.PadMaker(self.module).SMDPad(
            pad_length, pad_width, shape=pcbnew.PAD_SHAPE_RECT)

    def DrawBox(self, ssx, ssy):

        #  ----------
        #  |8 7 6 5 |
        #  |1 2 3 4 |
        #  \---------

        setback = pcbnew.FromMM(0.8)

        if setback > ssy:
            setback = ssy

        self.draw.BoxWithDiagonalAtCorner(0, 0, ssx*2, ssy*2, setback, self.draw.flipY)

SOICWizard().register()
