//
//  DiskController.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 14/07/2016.
//  Copyright © 2016 Thomas Harte. All rights reserved.
//

#include "DiskController.hpp"

#include "../../NumberTheory/Factors.hpp"

using namespace Storage::Disk;

Controller::Controller(Cycles clock_rate) :
		clock_rate_multiplier_(128000000 / clock_rate.as_int()),
		clock_rate_(clock_rate.as_int() * clock_rate_multiplier_),
		empty_drive_(new Drive((unsigned int)clock_rate.as_int(), 1, 1)) {
	// seed this class with a PLL, any PLL, so that it's safe to assume non-nullptr later
	Time one(1);
	set_expected_bit_length(one);
	set_drive(empty_drive_);
}

void Controller::set_component_is_sleeping(void *component, bool is_sleeping) {
	update_sleep_observer();
}

bool Controller::is_sleeping() {
	return !drive_ || drive_->is_sleeping();
}

void Controller::run_for(const Cycles cycles) {
	if(drive_) drive_->run_for(cycles);
}

Drive &Controller::get_drive() {
	return *drive_.get();
}

#pragma mark - Drive::EventDelegate

void Controller::process_event(const Track::Event &event) {
	switch(event.type) {
		case Track::Event::FluxTransition:	pll_->add_pulse();		break;
		case Track::Event::IndexHole:		process_index_hole();	break;
	}
}

void Controller::advance(const Cycles cycles) {
	if(is_reading_) pll_->run_for(Cycles(cycles.as_int() * clock_rate_multiplier_));
}

void Controller::process_write_completed() {
	// Provided for subclasses to override.
}

#pragma mark - PLL control and delegate

void Controller::set_expected_bit_length(Time bit_length) {
	bit_length_ = bit_length;
	bit_length_.simplify();

	Time cycles_per_bit = Storage::Time(clock_rate_) * bit_length;
	cycles_per_bit.simplify();

	// this conversion doesn't need to be exact because there's a lot of variation to be taken
	// account of in rotation speed, air turbulence, etc, so a direct conversion will do
	int clocks_per_bit = (int)cycles_per_bit.get_unsigned_int();
	pll_.reset(new DigitalPhaseLockedLoop(clocks_per_bit, 3));
	pll_->set_delegate(this);
}

void Controller::digital_phase_locked_loop_output_bit(int value) {
	if(is_reading_) process_input_bit(value);
}

void Controller::set_drive(std::shared_ptr<Drive> drive) {
	if(drive_ != drive) {
		bool was_sleeping = is_sleeping();
//		invalidate_track();

		if(drive_) {
			drive_->set_event_delegate(nullptr);
			drive_->set_sleep_observer(nullptr);
		}
		drive_ = drive;
		if(drive_) {
			drive_->set_event_delegate(this);
			drive_->set_sleep_observer(this);
		} else {
			drive_ = empty_drive_;
		}

		if(is_sleeping() != was_sleeping) {
			update_sleep_observer();
		}
	}
}

void Controller::begin_writing(bool clamp_to_index_hole) {
	is_reading_ = false;
	get_drive().begin_writing(bit_length_, clamp_to_index_hole);
}

void Controller::end_writing() {
	if(!is_reading_) {
		is_reading_ = true;
		get_drive().end_writing();
	}
}

bool Controller::is_reading() {
	return is_reading_;
}
