'use strict';

var q = require("q");

var SharedConstants = require('../../../shared/constants/SharedConstants');
var ServerConstants = require('../../constants/ServerConstants');
var ReferenceNoPreprocessor = require('./ReferenceNoPreprocessor');
var PREPROCESSORS_DIR = "./";
var logger = require('../../utils/Logger').loggers.get('db');
var UtilString = require('../../../shared/utils/UtilString');
var UtilType = require('../../../shared/utils/UtilType');

function getPreprocessor(field) {
    if(field.type && ServerConstants.PREPROCESSORS[field.type]) {
        return PREPROCESSORS_DIR + ServerConstants.PREPROCESSORS[field.type];
    } else if(field.componentName && ServerConstants.PREPROCESSORS[field.componentName]) {
        return PREPROCESSORS_DIR + ServerConstants.PREPROCESSORS[field.componentName];
    } else if(field.componentName && !field.componentName.startsWith("ft-")) {
        //preprocessor for custom component
        return PREPROCESSORS_DIR + ServerConstants.PREPROCESSORS[SharedConstants.FIELD_TYPE_CUSTOM];
    } else {
        return "";
    }
}



function isFieldChanged(field, operationType, updates, isDeepDiff) {
    if (operationType === SharedConstants.INSERT_OPERATION_TYPE) {
        // is an insert operation - field has changed
        return true;

    } else if (operationType === SharedConstants.UPDATE_OPERATION_TYPE) {
        if (isDeepDiff) {
            if (updates && updates.updates) {
                for (var j = 0; j < updates.updates.length; j++) {
                    var change = updates.updates[j];
                    if (change){
                        var fldName = UtilString.convertToObjectPathArray(change.path)[0];
                        if (field.name === fldName) {
                            // found in deepDiff updates - field has changed
                            return true;
                        }
                    }
                }
                // not found in deepDiff updates - field has not changed
                return false;

            } else {
                // expected deepDiff structure not present - assume field has changed
                return true;
            }

        } else {
            // document (not deepDiff) was passed in update - assume field has changed
            return true;
        }

    } else {
        // assume field has changed for now
        return true;
    }

}

function generateListComponents(templateComponents) {

  let listComponents = [];

  for (var i = 0; i < templateComponents.length; i++) {
      var component = templateComponents[i];

      listComponents.push(component);

      if (component.mainContent && component.mainContent.length) {
          listComponents = listComponents.concat(component.mainContent);
      }

      if (component.children && component.children.length) {
          listComponents = listComponents.concat(component.children);
      }
  }

  return listComponents;
}

function preProcessSaveFields(template, document, operationType, updates, isDeepDiff, userId) {
    var deferred = q.defer();

    var promises = [];
	var processorPath;

    // add each function to the array of promises. The preProcessing functions will need to return a promise
    if (template) {
        if (template.components) {
          const flatComponents = generateListComponents(template.components);
            for (var i = 0; i < flatComponents.length; i++) {
                var component = flatComponents[i];

                if (operationType === SharedConstants.INSERT_OPERATION_TYPE) {
                    let value = document[component.name];

                    if (UtilType.hasValue(component.defaultValue) && !UtilType.hasValue(value)) {
                        document[component.name] = component.defaultValue;
                    }
                }

                if (isFieldChanged(component, operationType, updates, isDeepDiff)) {
                    processorPath = getPreprocessor(component);
                    if (processorPath && processorPath !== '') {
                        var processor = require(processorPath);

                        logger.info('Adding preprocessor for ' + component.componentName);
                        // all preprocessors MUST have preProcesss(field, documnet) function
                        promises.push(processor.preProcess(component, document, userId));
                    } else {
                        logger.info(
                            'No preprocessor is defined for component: ' +
                                component.componentName +
                                '. Name: ' +
                                component.name +
                                ' at position ' +
                                i +
                                ' because there is a missing type or componentName.'
                        );
                    }
                }
            }
        }
    }

    // execute the promises
    if (promises.length > 0) {
        logger.info("Running preprocessors for document save. Number of preprocessors: " + promises.length);
        q.all(promises).done(function successFunc() {
            logger.info("Completed all preprocessors");
            deferred.resolve(promises.length);
        }, function errorFunc(err) {
            logger.error("Error pre-processing save fields");
            deferred.reject(err);
        });
    } else {
        logger.debug("No fields to preprocess");
        deferred.resolve(0);
    }

    return deferred.promise;
}

////////////////////////////////////
// public api
var PreProcessor = {

    preProcessSaveFields: preProcessSaveFields

};

module.exports = PreProcessor;

