--[[
  Copyright (c) 2023 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 Sci = require('common.block').getBlock(globals, cpu)
  if not static[cpu] then
    static[cpu] = {
      numInstances = 0,
      instances = {},
      finalized = false,
    }
  end
  Sci.instance = static[cpu].numInstances
  static[cpu].numInstances = static[cpu].numInstances + 1

  function Sci:createImplicit(params)
    U.enforceParamContract(
      params,
      {
        req = 'table',                          -- a module's resource requirement list
        rxGpio = U.isNonNegativeIntScalar,      -- GPIO pin for receiving data
        txGpio = U.isNonNegativeIntScalar,      -- GPIO pin for transmitting data
        opt_rxGpioStr = U.isString,             -- Full name of RX GPIO resource
        opt_txGpioStr = U.isString,             -- Full name of TX GPIO resource
        baud = U.isNonNegativeIntScalar,        -- Baud rate for SCI comm protocol
        unitAsChar = U.isChar,                  -- SCI unit
        opt_pinset = U.isNonNegativeIntScalar,  -- for legacy targets that use pinsets
        -- for modules without blocks, this string
        -- will be shown to users in the event that there is a resource
        -- conflict with aother module
        opt_userErrStr = U.isString,
      })

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

    self.sciBaud = params.baud
    self.sciPinset = params.opt_pinset
    self.unitAsChar = params.unitAsChar

    globals.syscfg:addEntry('sci', {
      unitAsChar = self.unitAsChar,
      pins = {params.rxGpio, params.txGpio},
      pinconf = {params.opt_rxGpioStr, params.opt_txGpioStr},
      core = self.cpu + 1
    })

    if params.opt_userErrStr then
      params.req:add('SCI %s' % {self.unitAsChar}, -1, params.opt_userErrStr)
      params.req:add('GPIO', params.rxGpio, params.opt_userErrStr..' (Rx)')
      params.req:add('GPIO', params.txGpio, params.opt_userErrStr..' (Tx)')
    else
      params.req:add('SCI %s' % {self.unitAsChar}, -1)
      params.req:add('GPIO', params.rxGpio)
      params.req:add('GPIO', params.txGpio)
    end
  end

  function Sci:p_getDirectFeedthroughCode()
    U.error('Explicit use of SCI via target block not supported.')
  end

  function Sci:finalize(c)
    -- make sure we only call this once for all SCI blocks
    if static[self.cpu].finalized then
      return
    end

    c.Include:append('plx_sci.h')

    c.Declarations:append([[
      PLX_SCI_Obj_t SciObjs[%(num_inst)d];
      PLX_SCI_Handle_t SciHandles[%(num_inst)d];
      ]] % {
      num_inst = static[self.cpu].numInstances,
    })

    -- instantiate unique SciHandle for each module, and
    -- use them to initialize a unique SciObj
    c.PreInitCode:append([[
    {
      for(int i = 0; i < %(num_inst)d; i++){
        SciHandles[i] =
          PLX_SCI_init(&SciObjs[i], sizeof(SciObjs[i]));
      }
    }
    ]] % {
      num_inst = static[self.cpu].numInstances
    })

    static[self.cpu].finalized = true
  end

  return Sci
end

return Module
