/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Customize the compiled style code into a styleSheet object, styleSheet object is divided into two parts, idSelectors
 * and classSelectors. There are some detailed rules to explain:
 * 1. Remove the "." And "#" symbols in front of the class selector and id selector;
 * 2. Convert all numeric strings to numbers, such as:"32px" convert to number 32; "11" convert to number 11;
 * 3. Convert all hex color to decimal number;
 * 4. Convert all boolean strings to boolean type;
 */
const {
  SPECIAL_STYLE,
  REGEXP_NUMBER_PX,
  REGEXP_COLOR,
  REGEXP_UNIT,
  REGXP_QUOTES,
} = require('./lite-enum');

/**
 * Split style into id Selectors and classSelectors.
 * @param {Object} value Preliminary compilation results of css files.
 * @return {String} String result stylesheet.
 */
function transformStyle(value) {
  const style = Function(`return ${value}`)();
  const idSelectors = {};
  const classSelectors = {};
  const styleSheet = {};
  let res = '';
  const KEYFRAMES = '@KEYFRAMES';
  const MEDIA_QUERY = '@MEDIA';
  const keys = Object.keys(style);
  for (const key of keys) {
    if (key.charAt(0) === '.') {
      classSelectors[key.slice(1)] = styleFormat(style[key]);
    } else if (key.charAt(0) === '#') {
      idSelectors[key.slice(1)] = styleFormat(style[key]);
    } else if (key === KEYFRAMES) {
      styleSheet['@keyframes'] = keyFrameFormat(style[key]);
    } else if (key === MEDIA_QUERY) {
      styleSheet['@media'] = mediaQueryFormat(style[key]);
    } else {

    }
  }
  if (style != null && keys.length !== 0) {
    if (Object.keys(idSelectors).length !== 0) {
      styleSheet['idSelectors'] = idSelectors;
    }
    if (Object.keys(classSelectors).length !== 0) {
      styleSheet['classSelectors'] = classSelectors;
    }
  }
  res = JSON.stringify(styleSheet);
  return res;
}

/**
 * keyFrame style special compilation.
 * @param {Object} obj Preliminary compilation results of keyFrame style.
 * @return {Object} keyFrame style object.
 */
function keyFrameFormat(obj) {
  for (const key of Object.keys(obj)) {
    const value = obj[key];
    for (const styleValue of value) {
      for (const styleKey of Object.keys(styleValue)) {
        const innerValue = styleValue[styleKey];
        if (REGEXP_COLOR.test(innerValue)) {
          styleValue[styleKey] = parseInt(innerValue.slice(1), 16);
        }
        try {
          styleValue[styleKey] = JSON.parse(styleValue[styleKey]);
        } catch (e) {
          // Values cannot be converted to objects are not processed
        }
      }
    }
  }
  return obj;
}

/**
 * media query special compilation.
 * @param {Array} mediaQueries the array of media query
 * @return {Array} media query style object
 */
function mediaQueryFormat(mediaQueries) {
  const target = [];
  for (const mediaQuery of mediaQueries) {
    const { condition, ...selectors } = mediaQuery;
    let style = transformStyle(JSON.stringify(selectors));
    if (style) {
      style = JSON.parse(style);
      target.push({ condition, ...style });
    }
  }
  return target;
}

const rules = [
  {
    match: function(key, value) {
      return (
        key === SPECIAL_STYLE.ANIMATION_DELAY ||
        key === SPECIAL_STYLE.ANIMATION_DURATION
      );
    },
    action: function(obj, key, value) {
      obj[key] = value;
    },
  },
  {
    match: function(key, value) {
      return key === SPECIAL_STYLE.ANIMATION_ITERATION_COUNT;
    },
    action: function(obj, key, value) {
      if (value === -1) {
        value = 'infinite';
      }
      obj[key] = value.toString();
    },
  },
  {
    match: function(key, value) {
      return [
        SPECIAL_STYLE.BACKGROUND_IMAGE,
        SPECIAL_STYLE.BACKGROUND_IMAGE_ACTIVE,
        SPECIAL_STYLE.BACKGROUND_IMAGE_CHECKED,
      ].includes(key);
    },
    action: function(obj, key, value) {
      obj[key] = value.replace(REGXP_QUOTES, '');
    },
  },
  {
    match: function(key, value) {
      return !isNaN(Number(value));
    },
    action: function(obj, key, value) {
      obj[key] = Number(value);
    },
  },
  {
    match: function(key, value) {
      return REGEXP_NUMBER_PX.test(value);
    },
    action: function(obj, key, value) {
      obj[key] = parseInt(value.replace(REGEXP_UNIT, ''), 10);
    },
  },
  {
    match: function(key, value) {
      return REGEXP_COLOR.test(value);
    },
    action: function(obj, key, value) {
      obj[key] = parseInt(value.slice(1), 16);
    },
  },
  {
    match: function(key, value) {
      return value === 'true';
    },
    action: function(obj, key, value) {
      obj[key] = true;
    },
  },
  {
    match: function(key, value) {
      return value === 'false';
    },
    action: function(obj, key, value) {
      obj[key] = false;
    },
  },
];

/**
 * Loop and format style, There are two rules defined here, types of number+px and color convert to number 16777215.
 * @param {Object} obj Preliminary compilation results of style object.
 * @return {Object} style object.
 */
function styleFormat(obj) {
  let value = '';
  for (const key of Object.keys(obj)) {
    value = obj[key];
    for (let i = 0; i < rules.length; i++) {
      if (rules[i].match(key, value)) {
        rules[i].action(obj, key, value);
        break;
      }
    }
  }
  return obj;
}

exports.transformStyle = transformStyle;