var knife = require('../knife');
var Issue = require('../issue');
var proc = require('../process_option');

module.exports = {
  name: 'component-attr-check',
  on: ['tag'],
  options: [{
    name: 'component-event-regex',
    desc: 'If set, tag\'s attributes that match this regex will be considered as an evnet listener.',
    process: proc.regex
  }, {
    name: 'component-prop-regex',
    desc: 'If set, tag\'s attributes that match this regex will be recognized as a component\'s property.',
    process: proc.regex
  }, {
    name: 'component-allow-attr',
    desc: 'A configuration object that holds all the components\' information of properties and events. All tags that are list in this object will go through this ruler.',
    process: proc.object
  }]
};

function toCamelcase(str = '') {
  return (typeof str === 'string') ? str.split('-').map((seg, index) => {
    return index != 0 ? seg[0].toUpperCase() + seg.substring(1) : seg;
  })
    .join('') : '';
}

function toDash(str = '') {
  return (typeof str === 'string') ? str.replace(/([A-Z])/g, (match, caps) => {
    return '-' + caps.toLowerCase();
  }) : '';
}


module.exports.lint = function (element, opts) {
  let components = opts['component-allow-attr'];
  let propRegex = opts['component-prop-regex'];
  let eventRegex = opts['component-event-regex'];
  let issueAttrs = [];
  let issues = [];

  if (components && components[element.name] && element.attribs) {

    let matchComponent = components[element.name];
    let propNames = matchComponent.props.map((prop) => prop.name);
    let requiredProps = matchComponent.props.filter((node) => node.required).map((node) => node.name);
    let events = matchComponent.events.map((method) => method.name);

    Object.entries(element.attribs).filter((attrib) => {
      if (knife.isCommonAttr(attrib[0]) || knife.isCmlDirective(attrib[0])) {
        return false;
      }
      if (eventRegex && eventRegex.test(attrib[0])) {
        let eventName = eventRegex.exec(attrib[0])[1];
        if (knife.isCommonEvent(eventName)) {
          return false;
        }
        return !~events.indexOf(eventName);
      }
      if (propRegex && propRegex.test(attrib[0])) {
        return !~propNames.indexOf(toCamelcase(propRegex.exec(attrib[0])[1]));
      }
      if (!propRegex && propNames.indexOf(toCamelcase(attrib[0])) > -1) {
        return false;
      }
      if (!eventRegex && events.indexOf(attrib[0]) > -1) {
        return false;
      }
      return true;
    })
    .forEach(attrib => {
      issueAttrs.push({
        ...attrib[1],
        _isEvent: eventRegex.test(attrib[0])
      });
    });

    if (requiredProps.length) {
      let attrKeys = Object.keys(element.attribs);
      requiredProps.forEach((propName) => {
        if (!~attrKeys.indexOf(toDash(propName))) {
          issues.push(new Issue('E073', element.openLineCol, {
            element: element.name,
            prop: toDash(propName)
          }));
        }
      });
    }
  }

  issues = issues.concat(issueAttrs ? issueAttrs.map((issueAttr) => {
    let propName = issueAttr._isEvent ? (eventRegex ? eventRegex.exec(issueAttr.name)[1] : issueAttr.name) : (propRegex ? propRegex.exec(issueAttr.name)[1] : issueAttr.name);
    return new Issue(issueAttr._isEvent ? 'E064' : 'E063', issueAttr.nameLineCol.map((lineCol) => {
      lineCol[1] = issueAttr.name.indexOf(propName) + 1;
      return lineCol;
    }), {
      name: element.name,
      prop: propName
    })
  }) : []);

  return issues;
};

@@ -1786,116 +1786,36 @@
 =%3E %7B
-%0A
      
- if (knife.isCommonAttr(attrib%5B0%5D) %7C%7C knife.isCmlDirective(attrib%5B0%5D)) %7B%0A        return false;%0A      %7D
+%0A      // verify events
 %0A   
@@ -2043,32 +2043,39 @@
 ntName);%0A      %7D
+      %0A
 %0A      if (propR
@@ -2069,20 +2069,22 @@
     if (
-prop
+!event
 Regex &&
@@ -2088,22 +2088,22 @@
  && 
-propRegex.test
+events.indexOf
 (att
@@ -2101,32 +2101,37 @@
 dexOf(attrib%5B0%5D)
+ %3E -1
 ) %7B%0A        retu
@@ -2137,40 +2137,167 @@
 urn 
-!~propNames.indexOf(toCamelcase(
+false;%0A      %7D%0A%0A      // verify normal attributes%0A      let attribProp = attrib%5B0%5D;%0A%0A      if (propRegex && propRegex.test(attribProp)) %7B%0A        attribProp = 
 prop
@@ -2317,17 +2317,16 @@
 trib
-%5B0%5D
+Prop
 )%5B1%5D
-))
 ;%0A  
@@ -2323,32 +2323,33 @@
 op)%5B1%5D;%0A      %7D%0A
+%0A
       if (!propR
@@ -2346,68 +2346,74 @@
 if (
-!propRegex && propNames.indexOf(toCamelcase(attrib%5B0%5D)) %3E -1
+knife.isCommonAttr(attribProp) %7C%7C knife.isCmlDirective(attribProp)
 ) %7B%0A
@@ -2438,24 +2438,25 @@
 se;%0A      %7D%0A
+%0A
       if (!e
@@ -2457,47 +2457,49 @@
 if (
-!eventRegex && events.indexOf
+propNames.indexOf(toCamelcase
 (attrib
-%5B0%5D
+Prop)
 ) %3E 
@@ -2529,24 +2529,26 @@
 lse;%0A      %7D
+ %0A
 %0A      retur
@@ -2779,16 +2779,209 @@
 ttribs);
+ %0A      attrKeys = attrKeys.map(attr =%3E %7B%0A        if (propRegex && propRegex.exec(attr))  %7B%0A          return propRegex.exec(attr)%5B1%5D;%0A        %7D else %7B%0A          return attr;%0A        %7D%0A      %7D);
 %0A      r

modify component-attr-check