--[[
  Copyright (c) 2021 by Plexim GmbH
  All rights reserved.

  A free license is granted to anyone to use this software for any legal
  non safety-critical purpose, including commercial applications, provided
  that:
  1) IT IS NOT USED TO DIRECTLY OR INDIRECTLY COMPETE WITH PLEXIM, and
  2) THIS COPYRIGHT NOTICE IS PRESERVED in its entirety.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
--]]--
local Module = {}
local U = require('common.utils')

local static = {}

function Module.getBlock(globals, cpu)

  local ExtSync = require('common.block').getBlock(globals, cpu)
  if static[cpu] == nil then
    static[cpu] = {
      numInstances = 0,
      instances = {},
      finalized = false,
    }
  end
  ExtSync["instance"] = static[cpu].numInstances
  static[cpu].numInstances = static[cpu].numInstances + 1

  function ExtSync:checkMaskParameters()
    if Block.Mask.Gpio ~= Block.Mask.Gpio or
        type(Block.Mask.Gpio) ~= 'number' then
      U.error('Invalid GPIO number.')
    end
  end

  function ExtSync:p_getDirectFeedthroughCode()
    local Require = ResourceList:new()
    local OutputSignal = StringList:new()
    
    table.insert(static[self.cpu].instances, self.bid)
    
    self.unit = Block.Mask.SyncUnit
    self.gpio = Block.Mask.Gpio
        
    Require:add('EXTSYNC', self.unit)
    
    globals.target.checkGpioIsValidPwmSync(self.gpio)
    
    OutputSignal:append("{synco = {bid = %i}}" % {ExtSync:getId()})

    return {
      Require = Require, 
      OutputSignal = {OutputSignal},
      UserData = {bid = self:getId()}
    }
  end

  function ExtSync:p_getNonDirectFeedthroughCode()
    local Require = ResourceList:new()
    if globals.target.getTargetParameters().epwms.type < 4 then
      -- dedicated sync pin - we need to claim it outright
      globals.target.allocateGpio(self.gpio, {}, Require)
    elseif not globals.target.isGpioAllocated(self.gpio) then
      -- configure pin implicitely as input
      local din_obj = self:makeBlock('din', self.cpu)
      din_obj:createImplicit(self.gpio, {}, Require)
    end

    if self:targetUsesDriverLib() then
      local input
      if self.unit == 1 then
        input = 5
      else
        input = 6
      end
      Require:add("XBAR_INPUT", input)
      globals.syscfg:addEntry('input_xbar', {
        gpio = self.gpio,
        input = input,
      })
    end

    return {
      Require = Require
    }
  end
  
  function ExtSync:finalizeThis(c)
    if globals.target.getTargetParameters().epwms.type < 4 then
      -- configure dedicated pin (for 69 and 335)
      local port
      if self.gpio == 6 then
        port = 'A'
      else
        port = 'B'
      end
      
      c.PreInitCode:append([[
        EALLOW;
        // configure external sync input
        GpioCtrlRegs.GP%(port)sMUX1.bit.GPIO%(gpio)i = 2;
        GpioCtrlRegs.GP%(port)sDIR.bit.GPIO%(gpio)i = 0;
        EDIS;
      ]] % {port = port, gpio = self.gpio})
    end
  end

  function ExtSync:finalize(c)

    if static[self.cpu].finalized then
      return
    end
    
    for _, bid in ipairs(static[self.cpu].instances) do
      local extsync = globals.instances[bid]
      if extsync:getCpu() == self.cpu then
        extsync:finalizeThis(c)
      end
    end
    
    static[self.cpu].finalized = true  
  end

  return ExtSync
end

return Module
