/**
 * 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");


module.exports={
    save:save,
    getData:getData,
    deleteByRation:deleteByRation,
    deleteByBill:deleteByBill,
    quantityEditChecking:quantityEditChecking,
};

let operationMap={
    'ut_create':create_quantity_detail,
    'ut_update':update_quantity_detail,
    'ut_delete':delete_quantity_detail
};

let updateFunctionMap = {
    'normalUpdate':normalUpdate,
    'updateQuantityRegex':updateQuantityRegex,
    'insertRecode':insertRecode
}

function create_quantity_detail(user_id,datas) {
    return function (callback) {
        let doc = datas.doc;
        doc.ID = uuidV1();
        if(doc.hasOwnProperty('regex')){
            insertRecodeWithReg(doc).then(function (resultObject) {
                if(resultObject.err){
                    callback(null,{
                        moduleName:consts.projectConst.QUANTITY_DETAIL,
                        err:{
                            message:result.err.message
                        }
                    });
                } else {
                    callback(null,resultObject.return_list);
                }
            })
        }else {
            createNormalRecode(doc,callback)
        }

    }
}

function insertRecode(user_id,datas) {
    return function (callback) {
        let doc = datas.doc;
        doc.ID = uuidV1();
        doInsertRecode(doc).then(function (result) {
            //console.log(result);
            if(result.err){
                callback(result.err,'')
            }else {
                callback(null,result.returndata)
            }

        })
    }
}

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;
}


function createNormalRecode(doc,callback) {
    quantity_detail_model.create(doc,(err,result)=>{
        if(err){
            callback(err,null);
        }else {
            console.log(result);
            let returndata ={
                updateTpye:commonConsts.UT_CREATE,
                moduleName:consts.projectConst.QUANTITY_DETAIL,
                data:result
            }
            callback(null,returndata)
        }
    });
}


async function insertRecodeWithReg (doc) {
    let returnObjec={
        err:null,
        return_list:[]
    }
    try {
        let returnData={
            moduleName:'',
            data:{
                updateTpye:commonConsts.UT_UPDATE,
            }
        }
        let regex = doc.regex.toUpperCase();
        let referenceIndexs = doc.referenceIndexs;
        let detailList = await getDatailList(doc,returnData);
        doc.result =getEvalResult(referenceIndexs,detailList,regex);
        let refreshQuantity =false;
        if(doc.refreshQuantity==true){
            refreshQuantity = true;
        }
        delete doc.refreshQuantity;
        let newRecode = await quantity_detail_model.create(doc) ;
        detailList.push(newRecode);
        if(refreshQuantity==true){
            returnData.data.quantity = await summateResuts(doc,detailList);
            returnData.data.quantityRefresh = true;
            returnObjec.return_list.push(returnData);
        }
        returnObjec.return_list.push({
            updateTpye:commonConsts.UT_CREATE,
            moduleName:consts.projectConst.QUANTITY_DETAIL,
            data:newRecode
        });
        return returnObjec;
    }catch (error){
        returnObjec.err = new Error('输入的表达式有误，请重新输入！');
        console.log(error)
        return returnObjec;
    }


}



function normalUpdate(user_id,datas) {
    return function(callback) {
        if(datas.doc.hasOwnProperty('isSummation')){
            doIsSummationUpdate(datas.query,datas.doc).then(function (sresult) {
                let returndata ={
                    moduleName:consts.projectConst.QUANTITY_DETAIL,
                    data:{
                        updateTpye:commonConsts.UT_UPDATE,
                        query:datas.query,
                        doc:datas.doc
                    }
                }
                let retrunArr = [];
                retrunArr.push(returndata);
                if(sresult){
                    retrunArr.push(sresult);
                }
                callback(null,retrunArr);
            })
        }else {
            updateRecored(datas.query,datas.doc,callback);
        }
    }
}
function updateQuantityRegex(user_id,datas) {
    return function(callback){
        console.log(datas);
        doRegexUpdate(datas).then(function (result) {
            if(result.err){
                callback(null,{
                    moduleName:consts.projectConst.QUANTITY_DETAIL,
                    err:{
                        message:result.err.message
                    }
                });
            }else {
                callback(null,result.rList);
            }

        })
    }
}

async function doIsSummationUpdate(query,doc) {
    try {
        let returnData={
            moduleName:'',
            data:{
                updateTpye:commonConsts.UT_UPDATE,
            }
        }
        let  refreshQuantity=false;
        if(query.refreshQuantity==true){
            refreshQuantity=true;
        }
        delete query.refreshQuantity;
        let updateDoc = await quantity_detail_model.update(query,doc);
        let detailList = await getDatailList(query,returnData);
        if(refreshQuantity==true){
            let quantity = await summateResuts(query,detailList);
            returnData.data.quantity = quantity;
            returnData.data.quantityRefresh = true;
            return returnData;
        }else {
            return null;
        }
    }catch (error){
        console.log(error)
    }
}

async function getDatailList(query,resultObject) {
    let detailList = [];
    if(query.hasOwnProperty('rationID')){
        detailList = await quantity_detail_model.find({'projectID':query.projectID,'rationID':query.rationID}).sort('seq').exec();
        resultObject.moduleName = consts.projectConst.RATION;
        resultObject.data.rationID=query.rationID;

    }else {
        detailList = await quantity_detail_model.find({'projectID':query.projectID,'billID':query.billID}).sort('seq').exec();
        resultObject.moduleName = consts.projectConst.BILLS;
        resultObject.data.billID=query.billID;
    }
    return detailList;
}


async function doRegexUpdate(datas) {
    let resultObjec ={
        err:null,
        rList:[]
    }
    try {
        let detailList = [];
        let quantityResult ={
            moduleName:'',
            data:{
                updateTpye:commonConsts.UT_UPDATE,
                quantityRefresh:true
            }
        }
        if(datas.query.hasOwnProperty('rationID')){
            detailList = await quantity_detail_model.find({'projectID':datas.query.projectID,'rationID':datas.query.rationID}).sort('seq').exec();
            quantityResult.moduleName = consts.projectConst.RATION;
            quantityResult.data.rationID = datas.query.rationID;
        }else {
            detailList = await quantity_detail_model.find({'projectID':datas.query.projectID,'billID':datas.query.billID}).sort('seq').exec();
            quantityResult.data.billID = datas.query.billID;
            quantityResult.moduleName = consts.projectConst.BILLS;
        }
        let regex;
        let result;
        if(datas.doc.regex==null){
            result=0
            datas.doc.referenceIndexs=[];
        }else {
            regex = datas.doc.regex.toUpperCase();
            let referenceIndexs = datas.doc.referenceIndexs;
            result =getEvalResult(referenceIndexs,detailList,regex);
        }
        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);
        let updateEdit = await quantity_detail_model.bulkWrite(generateUpdateTaks(updateTasks));
        resultObjec.rList.push(gernerateResultList(updateTasks));
        if(datas.query.refreshQuantity==true){
            quantityResult.data.quantity = await summateResuts(datas.query,detailList);
            resultObjec.rList.push(quantityResult);
        }
        return resultObjec;
    }catch (error){
        console.log(error);
        resultObjec.err=error;
        return resultObjec

    }
}

function gernerateResultList(updateTasks) {
    let returndata ={
        moduleName:consts.projectConst.QUANTITY_DETAIL,
        data:{
            updateTpye:commonConsts.UT_UPDATE,
            refreshList:updateTasks
        }
    }
    return returndata;

}



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 summateResuts (query,detailList) {
    let quantity = 0;
    for(let d of detailList){
        if(d.isSummation==1){
            let result = d.result==null||d.result==undefined?0:d.result
            quantity+=result;
        }
    }
    quantity = quantity.toFixed(4);
    if(query.hasOwnProperty('rationID')){
        await ration_model.update({'ID':query.rationID,'projectID':query.projectID,deleteInfo: null},{quantity:quantity,isFromDetail:1});
    }else {
        await bill_model.update({'ID':query.billID,'projectID':query.projectID,deleteInfo: null},{quantity:quantity,isFromDetail:1});
    }
    return quantity
}



function  getEvalResult(referenceIndexs,detailList,regex) {
    try {
        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 _.round(eval(regex), 4);
    }catch (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 updateRecored(query,doc,callback) {
    quantity_detail_model.update(query,doc,(err,result)=>{
        if(err){
            callback(err,'');
        }else {
            let returndata ={
                moduleName:consts.projectConst.QUANTITY_DETAIL,
                data:{
                    updateTpye:commonConsts.UT_UPDATE,
                    query:query,
                    doc:doc
                }
            }
            callback(null,returndata);
        }
    })
}


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

function delete_quantity_detail(user_id,datas) {
    return function (callback) {
        doQuantityDelete(datas.doc).then(function (result) {
            console.log(result);
            if(result.err){
                callback(result.err,'')
            }else {
                callback(null,result.returndata)
            }
        });
    }
}

async function doQuantityDelete(doc) {
   console.log(doc) ;
    let result={
        err:null
    }
    try{
        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,{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));
        }
        await quantity_detail_model.deleteOne({ID:doc.ID,projectID:doc.projectID});
        let returndata ={
            moduleName:consts.projectConst.QUANTITY_DETAIL,
            data:{
                doc:doc,
                resort:true,
                update_task:update_task,
                updateTpye:commonConsts.UT_DELETE
            }
        };
        result.returndata =returndata
        return result;
    }catch (error){
        console.log(error)
        result.err;
        return result
    }


}


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);
        });
    }
}

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);
    }
}