/**
 * Created by chen on 2017/7/20.
 */

/**
 * Created by chen on 2017/7/10.
 */
let mongoose = require('mongoose');
let consts = require('../../main/models/project_consts');
let commonConsts = consts.commonConst;
let _=require("lodash");
let async_n = require("async");
let quantity_detail_model = mongoose.model('quantity_detail');
const uuidV1 = require('uuid/v1');
let ration_model = mongoose.model('ration');
let bill_model=mongoose.model("bills");
const scMathUtil = require('../../../public/scMathUtil').getUtil();
let decimal_facade = require('./decimal_facade');

module.exports={
    save:save,
    getData:getData,
    deleteByRation:deleteByRation,
    deleteByBill:deleteByBill,
    quantityEditChecking:quantityEditChecking,
    saveQuantityDetail:saveQuantityDetail,
    update:update,
    updateRegex:updateRegex,
    insertRecode:insertRecode,
    deleteRecode:deleteRecode,
    swapRow:swapRow,
    deleteByQuery:deleteByQuery
};

let operationMap={
    'ut_update':update_quantity_detail,
};

let updateFunctionMap = {
    'insertRecode':insertRecode
}

async function saveQuantityDetail(datas) {
    let doc = datas;
    doc.ID = uuidV1();
    if(doc.refreshQuantity==false){ //如果选择了不替换工程量，则清空
         await cleanQuantityDetail(doc);
        return {};
    }
    if(doc.hasOwnProperty('regex')){
       return await insertRecodeWithReg(doc);
    }else {
        return await createNormalRecode(doc);
    }
}

async function update(datas) {
    let doc = datas.doc;
    if(doc.hasOwnProperty('isSummation')){
        return await doIsSummationUpdate(datas.query,doc);
    }else {
       await quantity_detail_model.update(datas.query,doc);
       return datas;
    }
}

async function swapRow(datas) {
    for(let task of datas){
        await update(task);
    }
    return {refreshList:datas};
}

async function updateRegex(datas) {
    let resultObjec ={};

    let detailList = [];
    let node ={};
    let decimal =await decimal_facade.getProjectDecimal(datas.query.projectID);
    if(datas.query.hasOwnProperty('rationID')){
        detailList = await quantity_detail_model.find({'projectID':datas.query.projectID,'rationID':datas.query.rationID}).sort('seq').exec();
        node.type = consts.projectConst.RATION;
        node.ID = datas.query.rationID;
    }else {
        detailList = await quantity_detail_model.find({'projectID':datas.query.projectID,'billID':datas.query.billID}).sort('seq').exec();
        node.type = consts.projectConst.BILLS;
        node.ID=datas.query.billID;
    }
    let regex;
    let result;
    if(datas.doc.regex==null){
        result=0;
        datas.doc.referenceIndexs=[];
    }else{
        if(datas.doc.hasOwnProperty('result')){//如果前端已经计算好了
            result = datas.doc.result;
        }else {
            regex = datas.doc.regex.toUpperCase();
            let referenceIndexs = datas.doc.referenceIndexs;
            result = getEvalResult(referenceIndexs,detailList,regex,decimal);
        }
    }
    detailList[datas.query.index].result =result;
    detailList[datas.query.index].regex=datas.doc.regex;
    detailList[datas.query.index].referenceIndexs =datas.doc.referenceIndexs;
    let updateTasks =[];
    datas.doc.result=result;
    updateTasks.push({
        query:{
            ID:datas.query.ID,
            projectID:datas.query.projectID
        },
        doc:datas.doc
    })
    updateReferenceRecode(datas.query.index+1,detailList,updateTasks);
    await quantity_detail_model.bulkWrite(generateUpdateTaks(updateTasks));
    resultObjec.refreshList=updateTasks;
    if(datas.query.refreshQuantity==true){
        let data = await summateResults(datas.query,detailList,decimal);
        node.data = data;
        resultObjec.node = node;
    }
    return resultObjec;

}


async function cleanQuantityDetail(doc) {
    let query = {
        projectID:doc.projectID
    }
    if(doc.hasOwnProperty('rationID')){
        query.rationID = doc.rationID;
    }else {
        query.billID = doc.billID;
    }
    return await quantity_detail_model.deleteMany(query);
}


async function insertRecode(doc){
    let query = {
        projectID:doc.projectID,
        seq : { $gte: doc.seq }
    }
    if(doc.hasOwnProperty('rationID')){
        query.rationID = doc.rationID;
    }else {
        query.billID = doc.billID;
    }
    let quantity_detail_List = await getDatailList(doc,{data:{}});
    let update_task = getUpdateReferenceTask(quantity_detail_List,doc.seq,1);
    await quantity_detail_model.update(query,{$inc:{seq:1}},{multi: true});
    if(update_task.length>0){
        await quantity_detail_model.bulkWrite(generateUpdateTaks(update_task));
    }
    doc.ID = uuidV1();
    let newrecode = await quantity_detail_model.create(doc);
    let data={
        doc:newrecode,
        resort:true,
        update_task:update_task
    }
    return data;
}

async function doInsertRecode(doc) {
    let result={
        err:null
    }
    try{
        let query = {
            projectID:doc.projectID,
            seq : { $gte: doc.seq }
        }
        if(doc.hasOwnProperty('rationID')){
            query.rationID = doc.rationID;
        }else {
            query.billID = doc.billID;
        }
        let quantity_detail_List = await getDatailList(doc,{data:{}});
        let update_task = getUpdateReferenceTask(quantity_detail_List,doc.seq,1);
        await quantity_detail_model.update(query,{$inc:{seq:1}},{multi: true});
        if(update_task.length>0){
            await quantity_detail_model.bulkWrite(generateUpdateTaks(update_task));
        }
        let newrecode = await quantity_detail_model.create(doc);
        let returndata ={
            updateTpye:commonConsts.UT_CREATE,
            moduleName:consts.projectConst.QUANTITY_DETAIL,
            data:{
                doc:newrecode,
                resort:true,
                update_task:update_task
            }
        };
        result.returndata =returndata
        return result;
    }catch (error){
        console.log(error)
        result.err;
        return result
    }
}

function getUpdateReferenceTask(quantity_detail_List,seq,re) {
    let update_task=[];
    for(let q of quantity_detail_List){
        let need_update =false;
        let newReg = q.regex;
        let newReferenceIndex;
        if(q.referenceIndexs.length>0){
            for (let i =0;i< q.referenceIndexs.length;i++){
                if(q.referenceIndexs[i]>seq){
                    newReg = replaceAll('C'+q.referenceIndexs[i],'C'+(q.referenceIndexs[i]+re),newReg);
                    newReg = replaceAll('c'+q.referenceIndexs[i],'c'+(q.referenceIndexs[i]+re),newReg);
                    q.referenceIndexs[i] +=re;
                    need_update = true;
                }
            }
        }
        if(need_update){
            newReferenceIndex = q.referenceIndexs;
            let task ={
                query:{
                    ID:q.ID,
                    projectID:q.projectID
                },
                doc:{
                    regex:newReg,
                    referenceIndexs:newReferenceIndex
                }
            }
            update_task.push(task);
        }
    }
    return update_task;
}


async function createNormalRecode(doc) {
    let result ={};
    result.newRecord= await quantity_detail_model.create(doc);
    return result;
}


async function insertRecodeWithReg (doc) {
    let returnObjec={}
    try {
        let decimal =await decimal_facade.getProjectDecimal(doc.projectID);
        let regex = doc.regex.toUpperCase();
        let referenceIndexs = doc.referenceIndexs;
        let detailList = await getDatailList(doc,returnObjec);
        if(!doc.hasOwnProperty('result')){//前端已结计算完了，后端就不用再计算了
            doc.result =getEvalResult(referenceIndexs,detailList,regex,decimal);
        }
        let refreshQuantity =false;
        if(doc.refreshQuantity==true){
            refreshQuantity = true;
        }
        delete doc.refreshQuantity;
        let newRecord = await quantity_detail_model.create(doc) ;
        detailList.push(newRecord);
        if(refreshQuantity==true){
            let data = await summateResults(doc,detailList,decimal);
            returnObjec.node.data = data;
        }
        returnObjec.newRecord = newRecord;
        return returnObjec;
    }catch (error){
        console.log(error);
        throw new Error('输入的表达式有误，请重新输入！');
    }
}

async function doIsSummationUpdate(query,doc) {
    let returnObjec={query:query,doc:doc};
    let decimal =await decimal_facade.getProjectDecimal(query.projectID);
    let  refreshQuantity=false;
    if(query.refreshQuantity==true){
        refreshQuantity=true;
    }
    delete query.refreshQuantity;
    await quantity_detail_model.update(query,doc);
    let detailList = await getDatailList(query,returnObjec);
    if(refreshQuantity==true){
        let data = await summateResults(query,detailList,decimal);
        returnObjec.node.data = data;
    }
    return returnObjec;
}

async function getDatailList(query,resultObject) {
    let detailList = [];
    let node={};
    if(query.hasOwnProperty('rationID')){
        detailList = await quantity_detail_model.find({'projectID':query.projectID,'rationID':query.rationID}).sort('seq').exec();
        node.type = consts.projectConst.RATION;
        node.ID = query.rationID;
    }else {
        detailList = await quantity_detail_model.find({'projectID':query.projectID,'billID':query.billID}).sort('seq').exec();
        node.type = consts.projectConst.BILLS;
        node.ID=query.billID;
    }
    resultObject.node = node;
    return detailList;
}


function updateReferenceRecode(index,detailList,updateTasks) {
    for(let d of detailList){
        if(_.includes(d.referenceIndexs,index)){
            let tResult = getEvalResult(d.referenceIndexs,detailList,d.regex);
            let t = {
                query:{
                    ID:d.ID,
                    projectID:d.projectID
                },
                doc:{
                    result:tResult
                }
            };
            d.result = tResult;
            updateTasks.push(t);
            updateReferenceRecode(d.seq+1,detailList,updateTasks);
        }
    }
}


async function summateResults (query,detailList,decimal) {
    let quantity = 0;
    let doc = {};
     decimal = decimal?decimal:await decimal_facade.getProjectDecimal(query.projectID);
    for(let d of detailList){
        if(d.isSummation==1){
            let result = d.result==null||d.result==undefined?0:d.result;
            quantity+=result;
        }
    }
    if(query.hasOwnProperty('rationID')){
        let ration = await  ration_model.findOne({'ID':query.rationID,'projectID':query.projectID,deleteInfo: null});
        let bill = await bill_model.findOne({'projectID':query.projectID,deleteInfo: null,"ID":ration.billsItemID});
        let bill_decimal = await decimal_facade.getBillsQuantityDecimal(query.projectID,bill.unit);
        let bill_quantity = scMathUtil.roundForObj(bill.quantity,bill_decimal);
        let contain = bill_quantity==0?0:scMathUtil.roundForObj(quantity/bill_quantity,decimal.process);
        let r_quantity = quantity;
        quantity = getQuantityByUnit(quantity,ration.unit);
        quantity = scMathUtil.roundTo(quantity, -decimal.ration.quantity);
        doc={quantity:quantity,isFromDetail:1,quantityEXP:'GCLMXHJ',contain:contain};
        await ration_model.update({'ID':query.rationID,'projectID':query.projectID,deleteInfo: null},doc);
        doc.r_quantity = r_quantity;
    }else {
        let bill = await bill_model.findOne({'ID':query.billID,'projectID':query.projectID,deleteInfo: null});
        decimal = await decimal_facade.getBillsQuantityDecimal(query.projectID,bill.unit);
       // quantity = getQuantityByUnit(quantity,bill.unit);
        quantity = scMathUtil.roundTo(quantity, -decimal);
        doc = {quantity:quantity,isFromDetail:1,quantityEXP:'GCLMXHJ'};
        await bill_model.update({'ID':query.billID,'projectID':query.projectID,deleteInfo: null},doc);
    }
    return doc
}

function getQuantityByUnit(quantity,unit) {
    if(unit){
        let times = parseInt(unit);
        if(isNaN(times)){
            times = 1
        }
        quantity = quantity/times;
    }
    return quantity;
}


 function  getEvalResult(referenceIndexs,detailList,regex,decimal={}) {
    try {
        decimal = decimal.quantity_detail?decimal.quantity_detail:4;
        for(let i of referenceIndexs){
            regex = replaceReference(i,detailList,regex)
        }
        console.log('replace all C reference -----'+regex);
        regex =replaceSqr(regex);
        console.log('replace all sqar reference -----'+regex);
        return scMathUtil.roundTo(eval(regex), -decimal);
    }catch (error){
        console.log(error);
        throw new Error('输入的表达式有误，请重新输入！');
    }

}

function  generateUpdateTaks(updateTasks) {
    var tasks=[];
    for(let u of updateTasks){
        let t ={
            updateOne:{
                filter:u.query,
                update: u.doc
            }
        }
        tasks.push(t);
    }
    return tasks;
}

function replaceReference(index,detailList,str) {
    str=str.toUpperCase();
    let rstr= detailList[index-1].regex==null?'0':'('+detailList[index-1].regex+')';
    str=replaceAll('C'+index,rstr,str);
    if(detailList[index-1].referenceIndexs.length>0){
        for (let i of detailList[index-1].referenceIndexs){
            str =replaceReference(i,detailList,str);
        }
    }
    return str;
}


function replaceAll (FindText, RepText,str) {
    let regExp = new RegExp(FindText, "g");
    return str.replace(regExp, RepText);
}


function replaceSqr(text) {
    var squarRegex = /\([^\^]+\)\^\d+/g;
    var sqararr = text.match(squarRegex);

    var squarRegex2 = /C[0-9]+\^\d+|[0-9]+([.]{1}[0-9]+){0,1}\^\d+/g; //匹配没有括号的
    var sqararr2=text.match(squarRegex2);
    if(sqararr){
        text=converSqrByArr(sqararr,text);
    }
    if(sqararr2){
        text=converSqrByArr(sqararr2,text);
    }
    return text;
}

function converSqrByArr (sqararr,text) {
    var temp = text;
    sqararr.forEach(function (item) {
        var arr = item.split('\^');
        var y = parseInt(arr[1]);
        var x_arr = [];
        for (var i = 0; i < y; i++) {
            x_arr.push(arr[0]);
        }
        var temStr = x_arr.join('*');
        temp = temp.replace(item, temStr);
    });
    return temp;
};

function update_quantity_detail(user_id,datas) {
    if(datas.updateFunction){
        return updateFunctionMap[datas.updateFunction](user_id,datas);
    }else {
        return normalUpdate(user_id,datas);
    }
}


async function deleteRecode(doc) {
    let resultObject = {};
    let decimal =await decimal_facade.getProjectDecimal(doc.projectID);

    let query = {
        projectID:doc.projectID,
        seq : { $gt: doc.seq }
    }
    if(doc.hasOwnProperty('rationID')){
        query.rationID = doc.rationID;
    }else {
        query.billID = doc.billID;
    }
    let quantity_detail_List = await getDatailList(doc,resultObject);
    let update_task = getUpdateReferenceTask(quantity_detail_List,doc.seq,-1);
    await quantity_detail_model.update(query,{$inc:{seq:-1}},{multi: true});
    if(update_task.length>0){
        await quantity_detail_model.bulkWrite(generateUpdateTaks(update_task));
    }
    await quantity_detail_model.deleteOne({ID:doc.ID,projectID:doc.projectID});
    _.remove(quantity_detail_List,{ID:doc.ID,projectID:doc.projectID});

    //更新节点信息
    let n_data = await summateResults(query,quantity_detail_List,decimal);
    resultObject.node.data = n_data;

    let r_data ={
            doc:doc,
            resort:true,
            update_task:update_task,
            updateTpye:commonConsts.UT_DELETE
        };
    resultObject.data = r_data;

    return resultObject;
}


function getData(projectID, callback) {
    quantity_detail_model.find({'projectID':projectID}).sort('seq').exec((err,datas)=>{
        if(err){
            callback(1, '', null);
        }else {
            callback(0, consts.projectConst.QUANTITY_DETAIL, datas);
        }
    })
}

function save (user_id, datas, callback) {
    let operations=[];
    if(_.isArray(datas)){
        for(let i=0;i<datas.length;i++){
            operations.push(operationMap[datas[i].updateType](user_id,datas[i]));
        }
    }else {
        operations.push(operationMap[datas.updateType](user_id,datas));
    }
    async_n.parallel(operations,function (err,results) {
        if(err){
            callback(err,'');
        }else {
            if(results.length==1){
                callback(null,results[0])
            }else {
                callback(null,results)
            }
        }
    })
}

function deleteByRation(data) {
    return function (callback) {
        quantity_detail_model.deleteMany({projectID: data.projectID, rationID: data.ID},(err,result)=>{
            commonCallback(callback,result,err);
        });
    }
}
function deleteByBill(data) {
    return function (callback) {
        console.log({projectID: data.projectID, billID: data.ID});
        quantity_detail_model.deleteMany({projectID: data.projectID, billID: data.ID},(err,result)=>{
            commonCallback(callback,result,err);
        });
    }
}

async function deleteByQuery(query) {
   return await  quantity_detail_model.deleteMany(query);
}

function quantityEditChecking(doc,type,functions) {
    if(doc.updateType == commonConsts.UT_UPDATE&&doc.updateData.hasOwnProperty('isFromDetail')&&doc.updateData.isFromDetail==0) {
        if(type=='bills'){
            functions.push(deleteByBill(doc.updateData));
        }else if(type=='ration'){
            functions.push(deleteByRation(doc.updateData));
        }
    }
}


function commonCallback(callback,result,err) {
    if(err){
        callback(err,'');
    }else {
        callback(null,result);
    }
}