declare var module : { id: string };

// Import the core angular services.
import { ControlValueAccessor } from "@angular/forms";
import { Directive } from "@angular/core";
import { forwardRef } from "@angular/core";
import { NG_VALUE_ACCESSOR } from "@angular/forms";

// Import the application services.
import { MyMoodComponent } from "./my-mood.component";

// I provide the ControlValueAccessor implementation for the MyMood component.
// --
// WHY BREAK THE VALUE ACCESSOR OUT INTO A DIFFERENT DIRECTIVE?
// This is a good question with a simple answer: it allows the MyMood component to be
// used with or without the FormsModule module. If the MyMood component implemented its
// own value-accessor, then you would have to include the FormsModule along with the
// MyMood component, even if the application wasn't actually using forms at all. Not only
// does this make the usage more flexible, it creates a cleaner separation of concerns.
@Directive({
	selector: "my-mood[ngModel]", // Notice [ngModel] selector.
	providers: [
		{
			provide: NG_VALUE_ACCESSOR,
			useExisting: forwardRef(
				function() {

					return( MyMoodFormDirective );

				}
			),
			multi: true
		}
	],
	host: {
		"(valueChange)": "handleValueChange( $event )"
	}
})
export class MyMoodFormDirective implements ControlValueAccessor {

	private onChangeCallback: any;
	private onTouchedCallback: any;
	private target: MyMoodComponent;


	// I initialize the directive.
	constructor( target: MyMoodComponent ) {

		this.target = target;

	}


	// ---
	// PUBLIC METHODS.
	// ---


	// I handle valueChange events emitted from the underlying DOM component.
	public handleValueChange( newValue: number ) : void {

		// With a Control Value Accessor, the whole intent is to synchronize the view-
		// model with the state of the DOM (Document Object Model). However, in this
		// case, since the "DOM" is actual another Angular component, we need to tell
		// the underlying component to render the new value. This way, the target DOM
		// will by synchronized with the change we are about to emit.
		// --
		// NOTE: This violates the one-way data flow; but, that's an expectation of the
		// ngModel directive usage.
		this.writeValue( newValue );

		// Synchronize the value from the DOM up into the view-model.
		this.onChangeCallback( newValue );

	}


	// I register the ngModel onChange callback.
	public registerOnChange( callback: any ) : void {

		this.onChangeCallback = callback;

	}


	// I register the ngModel onTouched callback.
	public registerOnTouched( callback: any ) : void {

		this.onTouchedCallback = callback;

	}


	// I write view-model values to the underlying DOM (synchronizing the value from
	// the view-model down into the DOM).
	public writeValue( value: any ) : void {

		// Store the new value back into the MyMood component.
		this.target.value = value;

		// ------------------------------------------------------- //
		// ---- BRIDGING THE ANGULAR 2.0.0. FUNCTIONALITY GAP ---- //
		// ------------------------------------------------------- //

		// At this point, Angular's ngModel implementation leaves things unfinished.
		// Specifically, it doesn't:
		//
		// - Run change-detection (needed for the OnPush change detection strategy).
		// - Run the ngOnChanges() life-cycle hook.
		//
		// You can bridge this gap yourself by manually running change-detection and
		// invoking the ngOnChanges() life-cycle hook. The change-detection is easy;
		// but, the ngOnChanges is not easy because the "isFirstChange()" implementation
		// uses a non-exported value, "UNINITIALIZED", which we would have to hack to
		// get it working.
		//
		// Read more: https://www.bennadel.com/blog/3092-creating-an-abstract-value-accessor-for-ngmodel-in-angular-2-beta-17.htm

	}

}