import { cloneDeep } from "lodash";
import { HistoryController } from "./historyCtrl";
import {BehaviorSubject, Subscription} from "rxjs";
import { reactive, toRaw } from  "vue";

export class ValueSnap {
    Id:string;
    Value: any;
    OldValue: any;

    Rx: BehaviorSubject<any>;
    constructor(id:string, value:any, oldValue:any, rx: BehaviorSubject<any>) {
        this.Id = id;
        this.Value = value;
        this.OldValue = oldValue;
        this.Rx = rx
    }
    redo() {
        this.Rx.next({value: this.Value, _hstry:false});
    }
    undo() {
        this.Rx.next({value: this.OldValue, _hstry:false});
    }

    clone() {
        return new ValueSnap(this.Id, this.Value,this.OldValue, this.Rx);
    }
}

export type RxValueType<T> = {
    value: T,
    _hstry?: boolean
}


function createRxValue<T>(value: T, histry:boolean) {
    return new BehaviorSubject< RxValueType<T> >({value:value,  _hstry: histry})
}

let _valueIndex = 0;
export function createValueSnap(value:any, oldValue:any, rx:BehaviorSubject<any>) {
  let i = _valueIndex + 1;
  _valueIndex +=1;
  return new ValueSnap(i+"", value, oldValue, rx);
}

class RxValue {
   static create<T extends {[key:string]: any}>(_fields:T, histroy?: HistoryController ) {
        let obj = {__rx:true} as any;
        
        obj._historySnap = {} as any;
        obj._historySub = {} as any;
        obj._rxs = {} as any;
        obj._fields = _fields;
        obj._history = histroy;
        obj._refs = {} as any;

        const names = Object.keys(_fields);
        
        names.forEach(name=>{


               const currName = name;
               const initValue = _fields[currName]

               const isRxField = typeof initValue == "object" && initValue.__rx
               if (isRxField) {
                    obj[currName] = initValue;
                    return;
               }

                const f = createRxValue(initValue, !!histroy);
                obj._rxs[name] = f;

                const snap = createValueSnap(initValue, initValue, f);
                obj._historySnap[name] = snap;
                const rxc =  reactive({value: initValue});

                Object.defineProperty(obj, currName, {
                    get: function(){
                        return rxc.value;
                    },
                    set: function(v) {
                        f.next({value: v});
                    },
                    configurable: true,
                    enumerable: true
                })

                const CamName = currName.slice(0,1).toUpperCase() +currName.slice(1);
                
                obj["set"+CamName] = function(value:T, nohistory = false){
                    f.next({value, _hstry: !nohistory});
                }
        
                obj["on"+CamName + "Changed"] = function(subscribe: (value:T, oldValue:T)=>void){
                    return f.subscribe((v:any)=>{
                        //if (CamName == "Transform") console.log("history 2222222222222222222222222222")
                            subscribe(v.value, snap.OldValue)
                        }
                    )
                }
                
                obj._historySub[name] = f.subscribe((v:any)=>{
                    //if (CamName == "Transform") console.log("history 11111111111111111111111111")
                    snap.OldValue = rxc.value;
                    rxc.value = v.value;
                    if (obj._history && obj._history.enable) {
                        if (!v._hstry) return;
                        const s = snap.clone();
                        s.Value = v.value;
                        obj._history.record(s);
                    }
                })
        });


        obj["setHistory"] = function(h: HistoryController){
            obj._history = h;
        }
        obj["toJson"] = function() {
            const out:any = {};
            const names = Object.keys(_fields);

            names.forEach(name=>{
                const initV = _fields[name]
                const isRxField = typeof initV == "object" && initV.__rx
                if (isRxField) {
                    out[name] = obj[name].toJson();
                    return;
                }
                out[name] =cloneDeep(toRaw(obj._rxs[name].getValue().value));
            })
            return out;
        }
        obj["fromJson"] = function(json:any) {
            const out:any = {};
            const names = Object.keys(_fields);
            names.forEach(name=>{
                const initV = _fields[name]
                const isRxField = typeof initV == "object" && initV.__rx
                if (isRxField ) {
                    if(json[name]) out[name] = obj[name].fromJson( json[name] );
                    return;
                }
                obj._rxs[name].next({value: json[name] != undefined ? json[name] : cloneDeep(initV) , _hstry: false})
            })
            return out;
        }
        
        type setHistoryType = (history: HistoryController)=>void;
        type toJSONType = ()=>typeof _fields;
        type fromJsonType = (json:typeof _fields)=>void;

        return obj as typeof _fields & {
            [K in keyof typeof _fields as `set${Capitalize<string & K>}`]: (value: typeof _fields[K], nohistory?:boolean) => void;
        } & {
            [K in keyof typeof _fields as `on${Capitalize<string & K>}Changed`]: (subscribe: (value: typeof _fields[K], oldValue:typeof _fields[K])=>void) => Subscription;
        } & 
        // {
        //     [K in keyof typeof _fields as `ref${Capitalize<string & K>}`]: () => typeof _fields[K];
        // } &
        {
            setHistory: setHistoryType
            toJson:toJSONType
            fromJson:fromJsonType
        }
    }
}

export {RxValue};