import is from '@pilotlab/is';
import { Initializable } from '@pilotlab/initializable';
import { IPromise, Result } from '@pilotlab/result';
import { AttributeChangeActions, DataType } from './attributes/attributeEnums';
import IAttribute from './attributes/interfaces/iAttribute';
import IAttributeChangeOptions from './attributes/interfaces/iAttributeChangeOptions';
import AttributeChangeOptions from './attributes/attributeChangeOptions';
import AttributeCreateOptions from './attributes/attributeCreateOptions';
import { Data } from './data';
import { IDataOwner } from './interfaces/iDataOwner';
import { IDataSetter } from './interfaces/iDataSetter';


export class DataSetter<
    TOwner extends IDataOwner,
    TData extends Data,
    TDataSetter extends DataSetter<any, any, any>> extends Initializable implements IDataSetter
{
    constructor() { super(); }


    initialize(
        dataOwner:TOwner,
        data:TData,
        changeOptions:IAttributeChangeOptions = AttributeChangeOptions.default.durationZero,
        isBatchStarted:boolean = false
    ):IPromise<any> { return this.p_initialize([dataOwner, data, changeOptions, isBatchStarted]); }
    protected p_onInitializeStarted(result:Result<any>, args:any[]):IPromise<any> {
        this.p_owner = args[0];
        this.p_data = args[1];
        this.p_changeOptions = args[2];
        this.p_isBatchStarted = args[4];
        return result.resolve();
    }


    /*====================================================================*
     START: Properties
     *====================================================================*/
    get owner():TOwner { return this.p_owner; }
    protected p_owner:TOwner;


    protected p_data:TData;


    get changeOptions():IAttributeChangeOptions { return this.p_changeOptions; }
    protected p_changeOptions:IAttributeChangeOptions;


    protected p_isBatchStarted:boolean;


    /*====================================================================*
     START: Methods
     *====================================================================*/
    register(key:string, defaultValue?:any, dataType?:DataType):IAttribute {
        return this.p_data.children.get(key, new AttributeCreateOptions(defaultValue, dataType));
    }


    create(
        dataOwner:TOwner,
        data:TData,
        changeOptions:IAttributeChangeOptions,
        isBatchStarted:boolean = false
    ):TDataSetter {
        let dataSetter:TDataSetter = <TDataSetter>new DataSetter<any, any, any>();
        dataSetter.initialize(dataOwner, data, changeOptions, isBatchStarted);
        return dataSetter;
    }


    start(changeOptions?:IAttributeChangeOptions, isBatch:boolean = false):TDataSetter {
        if (isBatch) this.p_isBatchStarted = true;

        if (is.empty(changeOptions)) changeOptions = this.changeOptions;
        else this.p_changeOptions = changeOptions;

        return this.create(this.owner, this.p_data, changeOptions, isBatch);
    }


    end():void {
        if (!this.p_isBatchStarted) return;
        this.p_isBatchStarted = false;
        if (this.changeOptions.isSave) this.p_data.autoSave();
    }


    protected p_update(attribute:IAttribute, value:any, changeOptions?:IAttributeChangeOptions):TDataSetter {
        if (is.empty(changeOptions)) changeOptions = this.changeOptions;
        attribute.set(value, this.p_isBatchStarted ? changeOptions.copy.setAction(AttributeChangeActions.NONE) : changeOptions);
        return this.create(this.owner, this.p_data, changeOptions, this.p_isBatchStarted);
    }
} // End class


export default DataSetter;
