import * as React from "react";
import {ReactElement} from "react";
import {GlobalContext, GlobalContextContent} from "../react/global-context";

export interface StatefulBaseComponentConstructor<P, S extends object> extends React.ComponentClass<P>, React.ComponentLifecycle<P, S> {
	new (props?: P, context?: any): StatefulBaseComponent<P, S>;
}

/* do not use this, without special problem */
export abstract class StatefulBaseComponent<P, S extends object> extends React.Component<P, S> implements GlobalContext {
	public readonly context: any;
	private static _bindingMethods; // not used any where
	
	public abstract state: S;
	
	constructor(props: P, context) {
		super(props, context);
		// console.log('constructor of', this.constructor.name);
		if (this.constructor.hasOwnProperty('_bindingMethods')) {
			this.constructor['_bindingMethods'].forEach((name) => {
				this[name] = this[name].bind(this);
			});
		}
		
		this.afterConstruct(props, context);
	}
	
	afterConstruct<T>(props: P, context: T) {
	}
	
	abstract render(): ReactElement<any>|null;
}

export interface BaseComponentConstructor<P> extends React.ComponentClass<P>, React.ComponentLifecycle<P, {}> {
	new (props?: P, context?: any): BaseComponent<P>;
}

export interface ReactComponentConstructor<P> extends React.ComponentClass<P>, React.ComponentLifecycle<P, any> {
	new (props?: P, context?: any): React.Component<P, any>;
}

/** Redux base component */
export abstract class BaseComponent<P> extends StatefulBaseComponent<P, {}> implements GlobalContext {
	state: {};
}

export function BindThis<T>(target: StatefulBaseComponent<any, any>,
                            propertyKey: string|symbol,
                            descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> {
	if (descriptor === undefined) {
		throw new TypeError('@BindThis only allow to decorate method.');
	}
	
	if (!target.constructor.hasOwnProperty('_bindingMethods')) {
		Object.defineProperty(target.constructor, '_bindingMethods', {
			configurable: true,
			enumerable: false,
			value: [],
		});
	}
	target.constructor['_bindingMethods'].push(propertyKey);
	
	// console.log('BindThis of', target.constructor.name);
	// console.log(target.constructor['_bindingMethods']);
	
	return descriptor;
}
