
#include "FootageBrowser.H"
#include "sl.h"
#include <lqt/lqt.h>
#include <lqt/colormodels.h>
#include <iostream>
#include <math.h>
#include <string>
#include <time.h>
#include <dirent.h>
#include <FL/filename.H>
FootageBrowser::FootageBrowser()
{
	m_last_used_clip = 0;
	m_position_in_clip = 0.0;
	m_current_file = "";
	m_files = 0;
	m_length = 0;
	for ( int i = 0; i < MAX_OPEN_FILES; i++ ) {
		m_open_files[i].empty = true;
	}
	m_bypass = false;
}

FootageBrowser::~FootageBrowser()
{
	clear();
}

void FootageBrowser::addFile( const char* filename )
{
	int64_t len = framesInFile( filename );
	if ( len == 0 ) {
		return;
	}
	file_list* p = new file_list;
	p->next = 0;
	p->filename = filename;
	p->length = len;
	p->this_file_open = 0;
	m_files = (file_list*)sl_push( m_files, p );
	//m_files = (file_list*)sl_unshift( m_files, p );
	m_length += p->length;
}
typedef struct _dir_list_node {
	struct _dir_list_node *next;
	std::string name;
} dir_node;


void FootageBrowser::addFolder( const char* folder )
{
	int n;
	dirent** list;
	dir_node* folders = 0;
	dir_node* p;
	n = scandir( folder, &list, 0, alphasort );
	if ( n <= 0 ) {
		return;
	}
	for ( int i = 0; i < n; i++ ) {
		if ( list[i]->d_name[0] != '.' ) {
			p = new dir_node;
			p->next = 0;
			p->name = std::string( folder ) +  "/" + list[i]->d_name;
			folders = (dir_node*)sl_push( folders, p );
		}
	}
	for ( int i = n; i > 0; ) { // This is some bad ass hacking style from the fltk manual ;)
		free( (void*)( list[--i] ) );
	}
	if ( n > 0 ) {
		free( (void*)list );
	}

	while( folders ) {
		p = (dir_node*)sl_pop( &folders );
		if ( fl_filename_isdir( p->name.c_str() ) ) {
			n = scandir( p->name.c_str(), &list, 0, alphasort );
			while( n-- ) {
				if ( list[n]->d_name[0] != '.'  ) {
					p = new dir_node;
					p->next = 0;
					p->name = std::string( folder ) +  "/" + list[n]->d_name;
					folders = (dir_node*)sl_push( folders, p );
				}
			}
			for ( int i = n; i > 0; ) { // This is some bad ass hacking style from the fltk manual ;)
				free( (void*)( list[--i] ) );
			}
			if ( n > 0 ) {
				free( (void*)list );
			}
		} else {
			addFile( p->name.c_str() );
		}
	}
}
frame_rgb* FootageBrowser::getNextFrame() {
	if ( m_last_used_clip ) {
		quicktime_decode_video( m_last_used_clip->quicktime, m_last_used_clip->rows, 0);
		apply_lut( &m_last_used_clip->frame );
		return &m_last_used_clip->frame;
	}
	return 0;
}
frame_rgb* FootageBrowser::getFrameFromLastClip( double position )
{
	if ( m_last_used_clip ) {
		quicktime_set_video_position( m_last_used_clip->quicktime, llrint( position * (m_last_used_clip->file_entry->length-1) ), 0 );
		quicktime_decode_video( m_last_used_clip->quicktime, m_last_used_clip->rows, 0);
		apply_lut( &m_last_used_clip->frame );
		return &m_last_used_clip->frame;
	}
	return 0;
}
frame_rgb* FootageBrowser::getFrame( int64_t position )
{
	/*
	Find File at Frame Position
	If File Open, read Frame and return,
	If File not Open, find open_file_slot
	If no open_slot find oldest slot, close that and open the new, read and
	  return frame
	if open_slot, open file, read and return frame;
	*/
	file_list* i = m_files;
	if ( !m_files ) {
		return 0;
	}
	int64_t cur_position = 0;
	cur_position = i->length;
	if ( position > m_length ) {
		return 0;
	}
	while ( i && cur_position < position ) {
		i = i->next;
		cur_position += i->length;
	}
	if ( !i ) {
		return 0;
	}
	if ( i->this_file_open ) {
		open_file* f = (open_file*)i->this_file_open;
		quicktime_set_video_position( f->quicktime, position - cur_position +i->length, 0 );
		quicktime_decode_video( f->quicktime, f->rows, 0);
		m_current_file = f->file_entry->filename;
		m_position_in_clip = ((float)(position - cur_position + i->length)) / ((float)i->length);
		m_last_used_clip = f;
		f->last_used = time(0);
		apply_lut( &f->frame );
		return &f->frame;
	} else {
		open_file* open_slot = 0;
		for ( int j = 0; j < MAX_OPEN_FILES; j++ ) {
			if ( m_open_files[j].empty ) {
				open_slot = &m_open_files[j];
				break;
			}
		}
		if ( !open_slot ) {
			time_t min = m_open_files[0].last_used;
			open_slot = &m_open_files[0];
			for ( int j = 0; j < MAX_OPEN_FILES; j++ ) {
				if ( m_open_files[j].last_used < min ) {
					min = m_open_files[j].last_used;
					open_slot = &m_open_files[j];
				}
			}
			quicktime_close( open_slot->quicktime );
			delete [] open_slot->data;
			delete [] open_slot->rows;
			open_slot->file_entry->this_file_open = 0;
		}
		if ( open_slot ) {
			open_slot->quicktime = lqt_open_read( i->filename.c_str() );
			i->this_file_open  = open_slot;
			lqt_set_cmodel( open_slot->quicktime, 0, BC_RGB888 );
			open_slot->file_entry = i;
			int w = quicktime_video_width( open_slot->quicktime, 0 );;
			int h = quicktime_video_height( open_slot->quicktime, 0 );
			open_slot->data = new unsigned char[3*w*h];
			open_slot->frame.data = open_slot->data;
			open_slot->frame.w = w;
			open_slot->frame.h = h;
			open_slot->rows = new unsigned char*[h];
			for ( int j = 0; j < h; j++ ) {
				open_slot->rows[j] = open_slot->data + w * 3 * j;
			}
			quicktime_set_video_position( open_slot->quicktime, position - cur_position +i->length, 0 );
			quicktime_decode_video( open_slot->quicktime, open_slot->rows, 0);
			open_slot->empty = false;
			m_current_file = open_slot->file_entry->filename;
			m_position_in_clip = ((float)(position - cur_position + i->length)) / ((float)i->length);
			m_last_used_clip = open_slot;
			open_slot->last_used = time(0);
			apply_lut( &open_slot->frame );
			return &open_slot->frame;
		} 
	}
	return 0;
}
int64_t FootageBrowser::getLength()
{
	return m_length;
}
void FootageBrowser::clear()
{
	for ( int j = 0; j < MAX_OPEN_FILES; j++ ) {
		if ( !m_open_files[j].empty ) {
			quicktime_close( m_open_files[j].quicktime );
			delete [] m_open_files[j].data;
			delete [] m_open_files[j].rows;
			m_open_files[j].file_entry->this_file_open = 0;
			m_open_files[j].empty = true;
		}
	}
	file_list* node;
	while ( ( node = (file_list*)sl_pop( &m_files ) ) ) {
		delete node;
	}
	m_length = 0;
	m_last_used_clip = 0;
	m_position_in_clip = 0.0;
	m_current_file = "";
}
int64_t FootageBrowser::framesInFile( const char* filename )
{
	//Quicktime stuff
	quicktime_t* qt = lqt_open_read( filename );
	if ( !qt ) {
		return 0;
	}
	int64_t len = quicktime_video_length( qt, 0 );
	quicktime_close( qt );
	return len;
}
void FootageBrowser::calcLength()
{
	m_length = 0;
	for ( file_list* i = m_files; i; i = i->next ) {
		m_length += i->length;
	}
}
void FootageBrowser::lift( float r, float g, float b )
{
	m_lgg.lift( r, g, b );
}
void FootageBrowser::gain( float r, float g, float b )
{
	m_lgg.gain( r, g, b );
}
void FootageBrowser::gamma( float r, float g, float b )
{
	m_lgg.gamma( r, g, b );
}
void FootageBrowser::apply_lut( frame_rgb* frame )
{
	if ( m_bypass ) {
		return;
	}
	int len = frame->w * frame->h;

	unsigned char* out = frame->data;
	for ( int i = 0; i < len; i++ ) {
		out[0] = m_lgg.m_red[out[0]];
		//green is constant
		out[1] = m_lgg.m_green[out[1]];
		out[2] = m_lgg.m_blue[out[2]];
		out += 3;
	}

}


