--[[
  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 U = require('common.utils')

local static = {}

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

  function ExtSync:checkMaskParameters()

    self.unitAsInt = U.passThrough(Block.Mask.SyncUnit)
    self.gpio = U.enforceMask(U.isNonNegativeIntScalar, 'Gpio')
  end

  function ExtSync:p_getDirectFeedthroughCode()
    local Require = ResourceList:new()

    table.insert(static[self.cpu].instances, self.bid)

    Require:add('EXTSYNC', self.unitAsInt)

    globals.target.checkGpioIsValidPwmSync(self.gpio)

    return {
      Require = Require,
      OutputSignal = {
        [1] = {'{synco = {bid = %i}}' % {ExtSync:getId()}}
      },
      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 implicitly as input
      local din_obj = self:makeBlock('din', self.cpu)
      din_obj:createImplicitDin(self.gpio, {}, Require)
    end

    if self:targetUsesDriverLib() then
      local input
      if self.unitAsInt == 1 then
        input = 5
      else
        input = 6
      end
      Require:add('XBAR_INPUT', input)
      globals.syscfg:addPeripheralEntry('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
