import dataTypes, { isDataTypeOf } from "./dataTypes";
import { hasOwnProperty, isToStringReferenceEqual } from "./objectUtils";

const compileClassNames = (...classinfoList)=>{
    let processedClassNameList = [];

    for (let index = 0; index < classinfoList.length; index++) {
      
      let classInfo = classinfoList[index];
     
      if (!classInfo){
        continue;
      } 

      if( isDataTypeOf(classInfo,dataTypes.string) || isDataTypeOf(classInfo,dataTypes.number) )
      {
        processedClassNameList.push(this && this[classInfo] || classInfo);
      }
      else if( isDataTypeOf(classInfo,dataTypes.array) ) 
      {
        processedClassNameList.push(compileClassNames.apply(this, classInfo));
      } 
      else if( isDataTypeOf(classInfo, dataTypes.object) ) 
      {
          if( isToStringReferenceEqual(classInfo) ) 
          {
              for (let property in classInfo) {
                  if (hasOwnProperty.call(classInfo, property) && classInfo[property]) {
                    processedClassNameList.push(this && this[property] || property);
                  }
              }
          } 
          else
          {
            processedClassNameList.push(classInfo.toString());
          }
      }
    }

    return processedClassNameList.join(' ');
}

export default compileClassNames;
//18-Nov-2021 code review changes updated.

/*
>>>>>>>>>>Code Review Suggestion Starts >>>>>>
let hasOwn = {}.hasOwnProperty;

Accounting readability, please change this to Object.hasOwnProperty

---
const compileClassNames = (...props)=>{
Please check the alternate term - collate -> collateClassNames -> Collect and Combine the classnames.
---

for (let i = 0; i < props.length; i++) {

    Please try to to stick with functional programming. Again for the readability perspective.
    Also, please stick with naming conventions for variables (i -> index).

---
if (argType === 'string' || argType === 'number') {
          classes.push(this && this[arg] || arg);
      } else if (Array.isArray(arg)) {
          classes.push(compileClassNames.apply(this, arg));
      } else if (argType === 'object') {
          if (arg.toString === Object.prototype.toString) {
              for (let key in arg) {
                  if (hasOwn.call(arg, key) && arg[key]) {
                      classes.push(this && this[key] || key);
                  }
              }
          } else {
              classes.push(arg.toString());
          }
      }
    }
In this code block, pushing up the array,object checks before string || number will be easy to follow up as it clarifies about the reursive call, its context.
Please check the possibility of avoiding the usage of 'this' statement due to its complexity in understanding the concept.

---
Please write a functional method for the below line for easier understanding 
  
if (arg.toString === Object.prototype.toString) {

---

Adding dedicated method to get the datatype of variables will help to stick on the SRP.
---
Check the possibility of providing platform to extend the algorithm works for other data structures like Map, WeakMap,etc..

>>>>>>> Code Review Suggestion Ends >>>>>>>>>>>>>>>>
*/

/*

>>>>>> Alternative Implementation Starts >>>>>>>>>>>>>>>> 

let boundHasOwnMethod = Object.hasOwnProperty;

 const collateClassNames = (...classinfoList) =>{
     let processedClassNameList = [];
     classinfoList.forEach( ( classInfo,index )=>{
         if(!classInfo)
         {
            // continue the next iteration
             return;
         }
         let dataTypeofClassInfo = getDataType(classInfo);
         if (dataTypeofClassInfo === 'string' || dataTypeofClassInfo === 'number')
         {
             processedClassNameList.push(classInfo);
         }
         else if(dataTypeofClassInfo === 'array')
         {
             processedClassNameList.push(collateClassNames(...classInfo));
         }
         else if(dataTypeofClassInfo === 'object')
         {
             for (let property in classInfo) {
                  if (boundHasOwnMethod.call(classInfo,property ) && classInfo[property]) {
                      processedClassNameList.push(property);
                  }
              }
         }
        

     });
     const joinClassNames = processedClassNameList.join(' ');
    return joinClassNames;

 }

 const getDataType = (data)=>{
    let dataTypeofClassInfo = typeof data;
    let isArrayDataType = Array.isArray(data) === true;
    if(isArrayDataType)
    {
        return 'array';
    }
    else 
    {
        return dataTypeofClassInfo;
    }
   
 };

>>>>>>>>>>>>>>>> Alternative Implementation Ends>>>>>>>>>>>>>> 
*/
