/*
 * Copyright 2016 Freescale Semiconductor
 * Copyright 2016-2017 NXP
 * To be used with KEx Tools. See Software License Agreement of KEx Tools.
 */

/**
 * Functions for making SDK structure field values from clock model state
 *
 */
 
// FieldVal object specifies object which is returned by SDKMapper.getFieldValue function.
var FieldVal = function (value, description, number) {
   this.value = value;
   this.description = description;
   this.number = number;
}; 
 
// SDKMapper object allows mapping a id to sdk enumeration symbol, a number or #define symbol (including "#define <symbol> ..." generation).
SDKMapper = {
      // Reference on define container in c file 
      setCWriterDefineContainer: null,
      
      // Internal function of SDKMapper object. This function adds a #define into container specified by setCWriterDefineContainer
      addCDefine: function (defineSymbol, defineValue, defineComment, formatOptions) {
        if (this.setCWriterDefineContainer != null) {
          this.setCWriterDefineContainer.addDefine(defineSymbol, defineValue, defineComment, formatOptions);
        }            
      },
      
      /* Gets value FieldVal (value, comment and number) for specified id. FieldVal.value can be formated according to parameter formatOptions.
       * Founded value (FieldVal.value) can be replaced by a symbolic constant according to defineSymbolname parameter.
       * cfg - reference to configuration object
       * fieldID - ID of item which should be mapped to number or enumaration or define symbol based on a setting
       * defineSymbolName - if null then no define is added into container specified by setCWriterDefineContainer
       *                  - if "" then define is added into the container named according to data in "mapping" part of this SDKMapper object and the alternative symbol is returned as .value
       *                  - if "<not_empty_string>" then define is added into the container named according to this parameter and the alternative symbol is returned as .value                   
       * formatOptions - determines format of result number in #define, e.g. ["hex"] or ["hex","unsigned"], see parameter options for function OutputUtils.formatField
       *                 it is ignored if defineSymbolName is null       
       * return value - object FieldVal 
       */       
      getFieldValue: function (cfg, fieldID, defineSymbolName, formatOptions) {
         var map = this.mapping[fieldID];               //default reference to the ID mapping object
         if (map == null) {
           scriptApi.logError("[DEBUG] Unsupported field ID " + fieldID + "."); 
           return null;
         }
         if (map[HwAbstr.getMcuFamily(Gen)] != null) {  //use the default reference if there is no Mcu series mapping object
           map = map[HwAbstr.getMcuFamily(Gen)];
         }
         if (formatOptions == null && map.formatOptions) {
           if ("formatOptions" in map) {
             formatOptions = map.formatOptions;
           }
         }
         switch (map.type) {
           case "enum":    
             var kval = map.keyFunc(cfg);
             if (kval == null) {
               scriptApi.logError("[DEBUG] " + map.keyFunc.toString() + " returns null."); 
               return null;
             }
             for (var i=0; i< map.enumMap.length; i++) {
               if (BigNumber.equal(map.enumMap[i][0], kval)) {
                 var ret = new FieldVal(map.enumMap[i][1], map.enumMap[i][2], map.enumMap[i][0]);
                 if (defineSymbolName != null) { 
                   if (defineSymbolName == "") { 
                     if (map.enumMap[i][3] != null && map.enumMap[i][3] != "") {
                       this.addCDefine(map.enumMap[i][1], map.enumMap[i][0], map.enumMap[i][2], formatOptions);
                     }
                   }
                   else {
                     this.addCDefine(defineSymbolName, map.enumMap[i][0], map.enumMap[i][2], formatOptions);
                     ret.value = defineSymbolName;
                   }
                 }
                 return ret;
               }
             }
             scriptApi.logError("[DEBUG] " + "enuMap for " + fieldID + " does not contain value " + kval + "."); 
             return null;
           case "number":
                 var ret = map.expr(cfg);
                 ret.number = ret.value;
                 if (defineSymbolName != null) {
                   if (defineSymbolName == "") { 
                     if ("defineSymbol" in map) {
                       var defineSymbol = map.defineSymbol(cfg, ret);
                       this.addCDefine(defineSymbol, ret.value, ret.description, formatOptions);
                       ret.value = defineSymbol;
                     }
                   }
                   else {
                     this.addCDefine(defineSymbolName, ret.value, ret.description, formatOptions);
                   }
                 }
                 return ret;
           case "multiEnum":
             // Usable for "mask" type enumerations. There can be enumerations which doesn't define anything for state =0. Then in case of there is no enum state in final code, a define needs to be created
             // Examples result code: "kMCG_PllEnableIndependent | kMCG_PllEnableInStop", "kMCG_PllEnableIndependent", "MCG_PLL_DISABLE_INDEPENDENT_DISABLE_IN_STOP"
             var code = "";
             var comment = "";
             // Iterate throught all functions in funcMap, create expression/comment from corresponding row in enumMap and add defines if there is "defineSymbol" in the row
             for (var i=0; i< map.funcMap.length; i++) {
               if (map.funcMap[i][0](cfg)) {
                 var enm = map.enumMap[map.funcMap[i][1]];
                 code = code + ((code == "") ? "" : " | ") + enm[1];
                 comment = comment + ((comment == "") ? "" : ", ") + enm[2];
                 if (enm[3] != null && enm[3] != "") {
                   if (defineSymbolName != null) {
                     this.addCDefine(enm[1], enm[0], enm[2], formatOptions);
                   }
                 }
               }
             }
             // If all functions returned zeros, use symbol "0"
             if (code == "") {
               if (map.enumMap[0] != null) {
                 code = code + ((code == "") ? "" : " | ") + map.enumMap[0][1];
                 comment = comment + ((comment == "") ? "" : ", ") + map.enumMap[0][2];
                 if (map.enumMap[0][3] != null && map.enumMap[0][3] != "") {
                   if (defineSymbolName != null) {
                     this.addCDefine(map.enumMap[0][1], map.enumMap[0][0], map.enumMap[0][2], formatOptions);
                   }
                 }
               }
               else {
                 scriptApi.logError("[DEBUG] " + "enuMap for " + fieldID + " does not contain value 0."); 
                 return null;
               }
             }
             if ("comment" in map) {
               comment = map.comment(cfg);
             }
             return new FieldVal(code, comment);
           default:                         
             return null;                  
         }
      },
      
      // Data part of this SDKMapper object. "mapping" contains field ids of certain type and theirs settings.
      // Type of the id can be "number", "enum" or "multiEnum".
      // defineSymbol in an enumMap table or defineSymbol function in <id> object ensures definiton of symbolic constant
      // e.g. there is defined "#define MCG_IRCLK_DISABLE   0U" for [0, "MCG_IRCLK_DISABLE",      "MCGIRCLK disabled", "defineSymbol"],
      //      or "#define RTC_OSC_CAP_LOAD_30PF   0x3c00" for "rtc.oscCapLoad" id
      mapping : {
		"armPllConfig.loopDivider": {
               type: "number",   
               expr: (function (cfg) {
                      var tmp = cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_ARM", "DIV_SELECT");
                        return new FieldVal(tmp, "PLL loop divider, Fout = Fin * " + cfg.getValueAsText("CCM_ANALOG.PLL1_VDIV.scale"));
                  })
        },
    "armPllConfig.src": {
               type: "number",   
               expr: (function (cfg) {
                      var tmp = cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_ARM", "BYPASS_CLK_SRC");
                        return new FieldVal(tmp, "Bypass clock source, 0 - OSC 24M, 1 - CLK1_P and CLK1_N");
                  })
        },
		"sysPllConfig.loopDivider": {
               type: "number",   
               expr: (function (cfg) {
                      var tmp = cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_SYS", "DIV_SELECT");
//                      tmp = 20 + tmp*2;
                        return new FieldVal(tmp, "PLL loop divider, Fout = Fin * ( 20 + loopDivider*2 + numerator / denominator )");
                  })
        },
		"sysPllConfig.numerator": {
               type: "number",   
               expr: (function (cfg) {
                      var tmp = cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_SYS_NUM", "A");
                        return new FieldVal(tmp, "30 bit numerator of fractional loop divider");
                  })
        },
		"sysPllConfig.denominator": {
               type: "number",   
               expr: (function (cfg) {
                      var tmp = cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_SYS_DENOM", "B");
                        return new FieldVal(tmp, "30 bit denominator of fractional loop divider");
                  })
        },
    "sysPllConfig.src": {
               type: "number",   
               expr: (function (cfg) {
                      var tmp = cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_SYS", "BYPASS_CLK_SRC");
                        return new FieldVal(tmp, "Bypass clock source, 0 - OSC 24M, 1 - CLK1_P and CLK1_N");
                  })
        },
		"usb1PllConfig.loopDivider": {
               type: "number",   
               expr: (function (cfg) {
//                      var tmp = cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_USB1", "DIV_SELECT");
//					  scriptApi.logWarning("configName " + tmp);
//                        return new FieldVal(tmp, "Divided by " + cfg.getValueAsText("CCM_ANALOG.PLL3_VDIV.scale"));
                        return new FieldVal(0, "PLL loop divider, Fout = Fin * " + cfg.getValueAsText("CCM_ANALOG.PLL3_VDIV.scale"));
                  })
        },
    "usb1PllConfig.src": {
               type: "number",   
               expr: (function (cfg) {
                      var tmp = cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_USB1", "BYPASS_CLK_SRC");
                        return new FieldVal(tmp, "Bypass clock source, 0 - OSC 24M, 1 - CLK1_P and CLK1_N");
                  })
        },
		"usb2PllConfig.loopDivider": {
               type: "number",   
               expr: (function (cfg) {
//                      var tmp = cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_USB2", "DIV_SELECT");
//                        return new FieldVal(tmp, "Divided by " + cfg.getValueAsText("CCM_ANALOG.PLL7_VDIV.scale"));
                        return new FieldVal(0, "PLL loop divider, Fout = Fin * " + cfg.getValueAsText("CCM_ANALOG.PLL7_VDIV.scale"));
                  })
        },
    "usb2PllConfig.src": {
               type: "number",   
               expr: (function (cfg) {
                      var tmp = cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_USB2", "BYPASS_CLK_SRC");
                        return new FieldVal(tmp, "Bypass clock source, 0 - OSC 24M, 1 - CLK1_P and CLK1_N");
                  })
        },
		"audioPllConfig.loopDivider": {
               type: "number",   
               expr: (function (cfg) {
                      var tmp = cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_AUDIO", "DIV_SELECT");
                        return new FieldVal(tmp, "PLL loop divider, Fout = Fin * ( loopDivider + numerator / denominator )");
                  })
        },
		"audioPllConfig.postDivider": {
               type: "number",   
               expr: (function (cfg) {
                      var tmp = cfg.getValueAsBigInteger("CCM_ANALOG.AUDIO_DIV.scale");
                          tmp = tmp*cfg.getValueAsBigInteger("CCM_ANALOG.PLL4_POST_DIV.scale");
                        return new FieldVal(tmp, "Divider after PLL");
                  })
        },
		"audioPllConfig.numerator": {
               type: "number",   
               expr: (function (cfg) {
                      var tmp = cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_AUDIO_NUM", "A");
                        return new FieldVal(tmp, "30 bit numerator of fractional loop divider, Fout = Fin * ( loopDivider + numerator / denominator )");
                  })
        },
		"audioPllConfig.denominator": {
               type: "number",   
               expr: (function (cfg) {
                      var tmp = cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_AUDIO_DENOM", "B");
                        return new FieldVal(tmp, "30 bit denominator of fractional loop divider, Fout = Fin * ( loopDivider + numerator / denominator )");
                  })
        },
    "audioPllConfig.src": {
               type: "number",   
               expr: (function (cfg) {
                      var tmp = cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_AUDIO", "BYPASS_CLK_SRC");
                        return new FieldVal(tmp, "Bypass clock source, 0 - OSC 24M, 1 - CLK1_P and CLK1_N");
                  })
        },
		"videoPllConfig.loopDivider": {
               type: "number",   
               expr: (function (cfg) {
                      var tmp = cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_VIDEO", "DIV_SELECT");
                        return new FieldVal(tmp, "PLL loop divider, Fout = Fin * ( loopDivider + numerator / denominator )");
                  })
        },
		"videoPllConfig.postDivider": {
               type: "number",   
               expr: (function (cfg) {
                      var tmp = cfg.getValueAsBigInteger("CCM_ANALOG.VIDEO_DIV.scale");
                          tmp = tmp*cfg.getValueAsBigInteger("CCM_ANALOG.PLL5_POST_DIV.scale");
                        return new FieldVal(tmp, "Divider after PLL");
                  })
        },
		"videoPllConfig.numerator": {
               type: "number",   
               expr: (function (cfg) {
                      var tmp = cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_VIDEO_NUM", "A");
                        return new FieldVal(tmp, "30 bit numerator of fractional loop divider, Fout = Fin * ( loopDivider + numerator / denominator )");
                  })
        },
		"videoPllConfig.denominator": {
               type: "number",   
               expr: (function (cfg) {
                      var tmp = cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_VIDEO_DENOM", "B");
                        return new FieldVal(tmp, "30 bit denominator of fractional loop divider, Fout = Fin * ( loopDivider + numerator / denominator )");
                  })
        },
    "videoPllConfig.src": {
               type: "number",   
               expr: (function (cfg) {
                      var tmp = cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_VIDEO", "BYPASS_CLK_SRC");
                        return new FieldVal(tmp, "Bypass clock source, 0 - OSC 24M, 1 - CLK1_P and CLK1_N");
                  })
        },
		"enetPllConfig.loopDivider": {
               type: "number",   
               expr: (function (cfg) {
                      var tmp = cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_ENET", "DIV_SELECT");
                        return new FieldVal(tmp, "Set frequency of ethernet reference clock to " + cfg.getValueAsText("CCM_ANALOG.ENET_DIV.outFreq"));
                  })
        },
		"enetPllConfig.enableClkOutput": {
               type: "enum",   
               keyFunc : (function (cfg) { return cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_ENET","ENABLE") }),
               enumMap:[ 
                     [0, "false",   "Disable the PLL providing the ENET 125MHz reference clock"],          
                     [1, "true",   "Enable the PLL providing the ENET 125MHz reference clock"],         
               ]  
        },
		"enetPllConfig.enableClkOutput25M": {
               type: "enum",   
               keyFunc : (function (cfg) { return cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_ENET","ENET_25M_REF_EN") }),
               enumMap:[ 
                     [0, "false",   "Disable the PLL providing the ENET 25MHz reference clock"],          
                     [1, "true",   "Enable the PLL providing the ENET 25MHz reference clock"],         
               ]  
        },
    "enetPllConfig.src": {
               type: "number",   
               expr: (function (cfg) {
                      var tmp = cfg.getBitFieldValueAsBigInteger("CCM_ANALOG::PLL_ENET", "BYPASS_CLK_SRC");
                        return new FieldVal(tmp, "Bypass clock source, 0 - OSC 24M, 1 - CLK1_P and CLK1_N");
                  })
        },
         "coreClock": {
           type: "number",   
           expr: (function (cfg) {                      
                     var freq = cfg.getValueAsText("AHB_CLK_ROOT.outFreq");
                     if (freq != "N/A") {                      
                       freq = cfg.getValueAsBigInteger("AHB_CLK_ROOT.outFreq"); 
                     }                      
                     return new FieldVal(freq, "Core clock frequency: " + freq + "Hz");  
                  })   
        },
      } // mapping  
};    
