// 物品表
import { decodeArrayListStr, readFileAndParse, parseGoodStr, parseNumberList, decodeArrayStr } from '../util'
import { FILENAME, IT_TYPE, ABI_TYPE, GOOD_TYPE } from '../../consts'
import { RewardInter } from '../interface';
const _ = require('lodash');
import { findWhere } from 'underscore';

export interface SpecialMaterial {
    readonly ids: number[];
    readonly count: number;
}

export interface DicGoods {
    // 物品id
    readonly good_id: number;
    // 物品名
    readonly name: string;
    // 等级限制
    readonly lvLimited: number;
    // 星级
    readonly equipLvl: number;
    // 职业限制
    readonly jobLimited: number[];
    // 武将限制
    readonly charLimited: number[];
    // 合成装备需要的碎片数
    readonly pieces: number;
    // 对应的碎片id
    readonly pieceId: number;
    // 合成材料
    readonly composeMaterial: Array<RewardInter>;
    // 特殊材料
    readonly specialMaterial: SpecialMaterial;
    // 分解所得
    readonly decomposeItem: Array<RewardInter>;
    // 物品品质
    readonly quality: number;
    // 洞数
    readonly hole: number;
    // 随机属性范围
    readonly randomEffect: Array<number>;

    // 类型id
    readonly itid: number;
    // 物品类型
    readonly goodType: number;
    // 将魂对应武将id
    readonly hid: number;
    // 属性
    readonly goodsAbility: Map<number, number>;
    // 强化属性
    readonly goodsAbilityUp: Map<number, number>;
    // 套装id
    readonly suitId: number;
    // 特殊属性
    readonly specialAttr: Map<number, number>;
    // 属性外加的值，经验，好感
    readonly value: number;

    readonly count?: number;

    readonly nextJewelId?: number;
    readonly specialCount?: number;
    readonly nextSpecialId?: number;
    // 对应的装备id
    readonly equipId?: number;
    // 解锁条件
    readonly condition: { id: number, type: number, params: number[] }[];
    // 时间限制
    readonly timeLimit: number;
    // 图片
    readonly image_id: number;
    // 礼包id
    readonly gift: number;
}

type KeysEnum<T> = { [P in keyof Required<T>]: true };
const DicGoodsKeys: KeysEnum<DicGoods> = {
    good_id: true,
    name: true,
    lvLimited: true,
    pieces: true,
    composeMaterial: true,
    specialMaterial: true,
    decomposeItem: true,
    quality: true,
    hole: true,
    randomEffect: true,
    itid: true,
    goodType: true,
    hid: true,
    goodsAbility: true,
    goodsAbilityUp: true,
    suitId: true,
    specialAttr: true,
    value: true,
    pieceId: true,
    count: true,
    nextJewelId: true,
    specialCount: true,
    nextSpecialId: true,
    equipId: true,
    condition: true,
    timeLimit: true,
    image_id: true,
    gift: true,
    jobLimited: true,
    charLimited: true,
    equipLvl: true
}
export const dicJewel = new Map<number, DicGoods>();
export const dicGoods = new Map<number, DicGoods>();
export const blueprtWithQuality = new Map<number, Array<number>>();
export const blueprtWithQualityAndStar = new Map<string, Array<number>>();
export const figureCondition = new Map<number, { params: number[], id: number, gid: number }[]>(); // type => {params, id, gid}

export function loadGoods() {
    dicJewel.clear();
    dicGoods.clear();
    blueprtWithQuality.clear();
    blueprtWithQualityAndStar.clear();
    figureCondition.clear();
    
    let arr = readFileAndParse(FILENAME.DIC_GOODS);

    arr.forEach(o => {
        o.goodsAbility = parseAbility(o);
        o.goodsAbilityUp = parseAbilityUp(o);
        o.composeMaterial = parseGoodStr(o.composeMaterial);
        o.decomposeItem = parseGoodStr(o.decomposeItem);
        o.specialAttr = parseSpecialAttr(o.specialAttr);
        o.specialMaterial = parseSpecialMaterial(o.specialMaterial);
        o.randomEffect = parseNumberList(o.randomEffect);
        o.timeLimit = o.timelimit;
        if (o.goodType == IT_TYPE.EQUIP_PIECE) {
            let good = findWhere(arr, { pieceId: o.good_id });
            if (!!good)
                o.equipId = good.good_id;
        }
        let condition = parseConditionStr(o.condition);
        for (let { id, type, params } of condition) {
            let mapArr = figureCondition.get(type) || new Array<{ params: number[], id: number, gid: number }>();
            mapArr.push({ params, id: id, gid: o.good_id });
            figureCondition.set(type, mapArr);
        }
        o.condition = condition;
        o.jobLimited = parseNumberList(o.jobLimited);
        o.charLimited = parseNumberList(o.charLimited);
        dicGoods.set(o.good_id, _.pick(o, Object.keys(DicGoodsKeys)));

        if (o.itid == IT_TYPE.BLUEPRT) {
            let arr = blueprtWithQualityAndStar.get(`${o.quality}_${o.equipLvl}`) || new Array<number>();
            arr.push(o.good_id);
            blueprtWithQualityAndStar.set(`${o.quality}_${o.equipLvl}`, arr);
            let arr2 = blueprtWithQuality.get(o.quality) || new Array<number>();
            arr.push(o.good_id);
            blueprtWithQuality.set(o.quality, arr2);
        } else if (o.goodType == GOOD_TYPE.JEWEL) {
            let material = o.composeMaterial[0];
            if (!!material && !!material.id) {
                let lastJewel = findWhere(arr, { good_id: material.id });
                if (!!lastJewel) {
                    lastJewel.count = material.count;
                    lastJewel.nextJewelId = o.good_id;
                    if (!!o.specialMaterial.ids[0]) {
                        lastJewel.specialCount = o.specialMaterial.count;
                        lastJewel.nextSpecialId = o.specialMaterial.ids[0];
                    }
                    dicJewel.set(lastJewel.good_id, _.pick(lastJewel, Object.keys(DicGoodsKeys)));
                }
            } else {
                dicJewel.set(o.good_id, _.pick(o, Object.keys(DicGoodsKeys)));
            }
        }
    });

    arr = undefined;
}

function parseSpecialAttr(str: string) {
    let specialAttr = new Map<number, number>();
    if (str) {
        let decodeArr = decodeArrayListStr(str);
        for (let [type, count] of decodeArr) {
            if (isNaN(parseInt(type)) || isNaN(parseInt(count))) {
                throw new Error('data table format wrong');
            }

            specialAttr.set(parseInt(type), parseInt(count));
        }
    }
    return specialAttr;
}

function parseAbility(json) {
    let map = new Map<number, number>();
    map.set(ABI_TYPE.ABI_HP, json.hp || 0);
    map.set(ABI_TYPE.ABI_ATK, json.atk || 0);
    map.set(ABI_TYPE.ABI_DEF, json.def || 0);
    map.set(ABI_TYPE.ABI_MDEF, json.mdef || 0);
    map.set(ABI_TYPE.ABI_DAMAGE_INCREASE, json.damageIncrease || 0);
    map.set(ABI_TYPE.ABI_DAMAGE_DECREASE, json.damageDecrease || 0);
    map.set(ABI_TYPE.ABI_PHYSICAL_DAMAGE_DECREASE, json.atkDecrease || 0);
    map.set(ABI_TYPE.ABI_MAGIC_DAMAGE_DECREASE, json.matkDecrease || 0);
    return map
}

function parseAbilityUp(json) {
    let map = new Map<number, number>();
    map.set(ABI_TYPE.ABI_HP, json.hp_up || 0);
    map.set(ABI_TYPE.ABI_ATK, json.atk_up || 0);
    map.set(ABI_TYPE.ABI_DEF, json.def_up || 0);
    map.set(ABI_TYPE.ABI_MDEF, json.mdef_up || 0);
    map.set(ABI_TYPE.ABI_DAMAGE_INCREASE, json.damageIncrease_up || 0);
    map.set(ABI_TYPE.ABI_DAMAGE_DECREASE, json.damageDecrease_up || 0);
    return map
}

function parseSpecialMaterial(str: string) {
    let specialAttr = { ids: new Array<number>(), count: 0 }
    if (!str) return specialAttr;

    let decodeArr = decodeArrayStr(str);
    if (decodeArr.length >= 2) {
        let ids = parseNumberList(decodeArr[0]);
        let count = parseInt(decodeArr[1]);
        if (isNaN(count)) return specialAttr;
        specialAttr.ids = ids;
        specialAttr.count = count;
    }
    return specialAttr;
}

// 解析物品 {"type": number, "param": number} 格式
export function parseConditionStr(str: string) {
    let result = new Array<{ id: number, type: number, params: number[] }>();
    if (!str) return result;
    let decodeArr = decodeArrayListStr(str);
    for (let i = 0; i < decodeArr.length; i++) {
        let [type, ...params] = decodeArr[i];

        if (isNaN(parseInt(type))) {
            throw new Error('data table format wrong');
        }
        let parsedParam = new Array<number>();
        for (let param of params) {
            if (isNaN(parseInt(param))) {
                throw new Error('data table format wrong');
            }
            parsedParam.push(parseInt(param))
        }
        result.push({ id: i + 1, type: parseInt(type), params: parsedParam });
    }
    return result
}
