import {Path} from './Path';
import {PushPopType} from './PushPop';
import {StringBuilder} from './StringBuilder';
import {InkObject} from './Object';
import {Pointer} from './Pointer';
import {Container} from './Container';
import {throwNullException} from './NullException';

export class Divert extends InkObject{
	get targetPath(){
		if (this._targetPath != null && this._targetPath.isRelative) {
			let targetObj = this.targetPointer.Resolve();
			if (targetObj) {
				this._targetPath = targetObj.path;
			}
		}

		return this._targetPath;
	}
	set targetPath(value: Path | null){
		this._targetPath = value;
		this._targetPointer = Pointer.Null;
	}

	public _targetPath: Path | null = null;

	get targetPointer(){
		if (this._targetPointer.isNull) {
			let targetObj = this.ResolvePath(this._targetPath).obj;

			if (this._targetPath === null) return throwNullException('this._targetPath');
			if (this._targetPath.lastComponent === null) return throwNullException('this._targetPath.lastComponent');

			if (this._targetPath.lastComponent.isIndex) {
				if (targetObj === null) return throwNullException('targetObj');
				this._targetPointer.container = (targetObj.parent instanceof Container) ? targetObj.parent : null;
				this._targetPointer.index = this._targetPath.lastComponent.index;
			} else {
				this._targetPointer = Pointer.StartOf((targetObj instanceof Container) ? targetObj : null);
			}
		}

		return this._targetPointer.copy();
	}

	public _targetPointer: Pointer = Pointer.Null;

	get targetPathString(){
		if (this.targetPath == null)
			return null;

		return this.CompactPathString(this.targetPath);
	}
	set targetPathString(value: string | null){
		if (value == null) {
			this.targetPath = null;
		} else {
			this.targetPath = new Path(value);
		}
	}

	public variableDivertName: string | null = null;
	get hasVariableTarget(){
		return this.variableDivertName != null;
	}

	public pushesToStack: boolean = false;
	public stackPushType: PushPopType = 0;

	public isExternal: boolean = false;
	public externalArgs: number = 0;

	public isConditional: boolean = false;

	constructor(stackPushType?: PushPopType){
		super();
		this.pushesToStack = false;

		if (typeof stackPushType !== 'undefined') {
			this.pushesToStack = true;
			this.stackPushType = stackPushType;
		}
	}

	public Equals(obj: Divert | null){
		let otherDivert = obj;
		if (otherDivert instanceof Divert) {
			if (this.hasVariableTarget == otherDivert.hasVariableTarget) {
				if (this.hasVariableTarget) {
					return this.variableDivertName == otherDivert.variableDivertName;
				} else {
					if (this.targetPath === null) return throwNullException('this.targetPath');
					return this.targetPath.Equals(otherDivert.targetPath);
				}
			}
		}
		return false;
	}

	public toString(){
		if (this.hasVariableTarget) {
			return 'Divert(variable: ' + this.variableDivertName + ')';
		}
		else if (this.targetPath == null) {
			return 'Divert(null)';
		} else {

			let sb = new StringBuilder();

			let targetStr = this.targetPath.toString();
			// int? targetLineNum = DebugLineNumberOfPath (targetPath);
			let targetLineNum = null;
			if (targetLineNum != null) {
				targetStr = 'line ' + targetLineNum;
			}

			sb.Append('Divert');

			if (this.isConditional)
				sb.Append('?');

			if (this.pushesToStack) {
				if (this.stackPushType == PushPopType.Function) {
					sb.Append(' function');
				} else {
					sb.Append(' tunnel');
				}
			}

			sb.Append(' -> ');
			sb.Append(this.targetPathString);

			sb.Append(' (');
			sb.Append(targetStr);
			sb.Append(')');

			return sb.toString();
		}
	}
}
