package com.freetmp.mbg.plugin.page;

import org.apache.commons.lang3.StringUtils;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.Element;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by LiuPin on 2015/2/4.
 */
public class SQLServerPaginationPlugin extends AbstractPaginationPlugin {

  int findElementIndex(List<Element> elements, String name, String key, String value) {
    int index = -1;
    for (int i = 0; i < elements.size(); i++) {
      Element element = elements.get(i);
      if (element instanceof XmlElement) {
        XmlElement xe = (XmlElement) element;
        if (matched(xe, name, key, value)) {
          index = i;
          break;
        }
      }
    }
    return index;
  }

  boolean matched(XmlElement element, String name, String key, String value) {
    if (element == null) return false;
    if (name.trim().equals(element.getName().trim())) {
      List<Attribute> attributes = element.getAttributes();
      if (attributes != null) {
        for (Attribute attribute : attributes) {
          if (StringUtils.equals(attribute.getName().trim(), key.trim())
              && StringUtils.equals(attribute.getValue().trim(), value.trim())) {
            return true;
          }
        }
      }
    }
    return false;
  }

  Element findElement(List<Element> elements, String name, String key, String value) {
    int index = findElementIndex(elements, name, key, value);
    Element element = elements.get(index);
    return element;
  }

  @Override
  public boolean sqlMapSelectByExampleWithoutBLOBsElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {

    XmlElement orderBy = (XmlElement) findElement(element.getElements(), "if", "test", "orderByClause != null");
    XmlElement baseColumnList = (XmlElement) findElement(element.getElements(), "include", "refid", introspectedTable.getBaseColumnListId());

    XmlElement prefix = buildPrefix(baseColumnList);
    XmlElement inner = buildInner(orderBy, introspectedTable);
    XmlElement suffix = buildSuffix(orderBy);


    List<Element> elements = element.getElements();
    List<Element> newElements = new ArrayList<>();

    newElements.add(prefix);
    for (Element e : elements) {
      if(e != orderBy) {
        newElements.add(e);
        if( e == baseColumnList){
          newElements.add(inner);
        }
      }
    }
    newElements.add(suffix);

    elements.clear();
    elements.addAll(newElements);
    return true;
  }

  private XmlElement buildInner(XmlElement orderBy, IntrospectedTable introspectedTable) {
    // generate order by clause, first check if user provided order by clause, if provided just use it
    // otherwise use the default primary key for order by clause
    XmlElement newOrderBy = new XmlElement("choose");
    XmlElement when = new XmlElement("when");
    when.addAttribute(new Attribute("test", "orderByClause != null"));
    for (Element e : orderBy.getElements()) {
      when.addElement(e);
    }
    newOrderBy.addElement(when);

    XmlElement otherwise = new XmlElement("otherwise");
    StringBuilder sb = new StringBuilder();
    sb.append(" order by ");
    List<IntrospectedColumn> columns = introspectedTable.getPrimaryKeyColumns();
    for (IntrospectedColumn column : columns) {
      sb.append(MyBatis3FormattingUtilities.getAliasedEscapedColumnName(column)).append(", ");
    }
    sb.setLength(sb.length() - 2);
    otherwise.addElement(new TextElement(sb.toString()));
    newOrderBy.addElement(otherwise);

    XmlElement inner = new XmlElement("if");
    inner.addAttribute(new Attribute("test", "limit != null and limit>=0 and offset != null"));
    inner.addElement(new TextElement(" , ROW_NUMBER() over ( "));
    inner.addElement(newOrderBy);
    inner.addElement(new TextElement(" ) as row_num "));
    return inner;
  }

  private XmlElement buildSuffix(XmlElement orderBy) {
    XmlElement suffix = new XmlElement("choose");

    XmlElement when = new XmlElement("when");
    when.addAttribute(new Attribute("test", "limit != null and limit>=0 and offset != null"));
    when.addElement(new TextElement(" ) as tmp where tmp.row_num between #{offset} and #{limit} + #{offset} order by row_num"));
    suffix.addElement(when);

    XmlElement otherwise = new XmlElement("otherwise");
    otherwise.addElement(orderBy);
    suffix.addElement(otherwise);
    return suffix;
  }

  private XmlElement buildPrefix(XmlElement baseColumnList) {
    XmlElement prefix = new XmlElement("if");
    prefix.addAttribute(new Attribute("test", "limit != null and limit>=0 and offset != null"));
    prefix.addElement(new TextElement("select "));
    prefix.addElement(baseColumnList);
    prefix.addElement(new TextElement(" from ( "));
    return prefix;
  }
}