#!/usr/bin/perl



use Mojolicious::Lite -signatures;
use Mojolicious::Static;
use Mojo::Log;
use Mojo::File;
use Mojo::JSON qw(decode_json encode_json);
use Encode qw(encode decode);
use List::Util qw(shuffle);
use Data::Dumper;
use Data::UUID;
use Mojo::Util qw/term_escape html_unescape url_unescape url_escape/;
use File::Find;
use File::Slurp;
plugin 'RenderFile';
use LWP::UserAgent;
use Mojolicious::Sessions;
use URI::Encode qw(uri_encode uri_decode);
use MIME::Base64;
use strict;

my $com = 'echo $HOME';
my $cwd = `$com`;
chomp $cwd;

require "./subroutines.pl";
my $device = &subs::device_setter();
my $config_file = read_file('./config.json');
my $config = decode_json $config_file;
my $environment = $config->{'environment'};
my @folders = @{$config->{'folders'}};
our $logfile = &subs::home($config->{'logfile'});
`touch $logfile` if not -e $logfile;
our $log = Mojo::Log->new(path => $logfile);
my (@results);
my $files = [];
my $thumb_files = [];
my $permissive = 0;
sub process_file() {
	my $name = $File::Find::name;
	if ($name =~ m/mp3$|m4a$|wav$|flac$|mp4$|mov$|webm$|weba$|wmv$/i) {
		$name =~ s/^\.\///g;
		push @{$files}, $name;
	}
	if ($permissive == 1) {
		if ($name =~ m/mp3\.enc$|m4a\.enc$|wav\.enc$|flac\.enc$|mp4\.enc$|mov\.enc$|webm\.enc$|weba\.enc$|wmv\.enc$/i) {
			$name =~ s/^\.\///g;
			push @{$files}, $name;
		}
	}
}

sub process_thumbfile() {
	my $name = $File::Find::name;
	if ($name =~ m/jpg$|png$/i) {
		$name =~ s/^\.\///g;
		push @{$thumb_files}, $name;
	}
}

get '/manager/music' => sub ($c) {
	my $search = $c->param('search') || &subs::setting_grabber({ app => 'music', 'setting' => 'search' });
	my $window_maker = $c->param('window_maker');
	my $unlock = &subs::setting_grabber({ app => 'music', setting => 'combo_unlock' });
	my $port = $c->req->url->base->port;
	my $misses = eval { return decode_json $c->param('misses') } || {};
	if (!$misses->{'video'}) {
		$misses->{'video'} = &subs::random_string_creator(25,"Aa");
	}
	$files = undef;
	my $settings = &subs::settings_grabber({ app => 'music' });
	$settings->{'artist'} = eval { return decode_json $settings->{'artist'} } || [];
	$settings->{'album'} = eval { return decode_json $settings->{'album'} } || [];
	if ($settings->{'library'} ne 'local' && $c->param('remoted') ne 'yes') {
		my $rm = &subs::db_query('select * from remote_machines where uuid=? and connection=?', $settings->{'library'}, 'active')->hashes->[0];
		if ($rm->{'uuid'}) {
			$c->param('remote_uuid' => $rm->{'uuid'});
			$c->param('subprocess' => 'yes');
			my $result = &Manager::remote_relay_request($c);
			if ($result =~ /music/gi) {
				$c->render(text => $result);
				return;
			}
		}
		else { &subs::setting_setter({ app => 'music', setting => 'library', value => 'local' }); $settings->{'library'} = 'local'; }
	}
	my $misc_settings = &Manager::misc_setting_list();
	if (my $cache = &subs::cache_get({ app => 'music', context => 'content' })) {
		if ($cache->{'contents'}) {
			if ($c->param('window_maker')) {
				my $website = &Manager::window_maker({ app => 'music', title => 'music', contents => $cache->{'contents'} }, &subs::rightNow());
				$c->render(text => $website);
			}
			else {
				$c->render(text => $cache->{'contents'});
			}
			return;
		}
	}
	if ($unlock) {
		if (secure_compare(&subs::note_decrypter($c->session('suds'), $unlock), &subs::note_decrypter($c->session('suds'), &subs::setting_grabber({ app => 'music', setting => 'combo_unlock' })) ) ) {
			$permissive = 1;
		}
	}
	my $crate = &music_search({ settings => $settings, c => $c, search => $search, now_playing => {}, port => $port, misc_settings => $misc_settings });
	my $timestamp = &subs::rightNow();
	my $jmao = &subs::setting_grabber({ app => 'music', setting => 'mao' });
	my $player = &subs::cache_get({ app => 'music', context => 'player' });
	my $resizes = eval { return decode_json $settings->{'resizes'} } || {};
	my $contents;
	if ($c->param('window_maker')) {
		$contents = $c->render_to_string(
			crate => $crate,
			interface_colour => &subs::random_colour_grabber(),
			template => 'apps/music/music',
			window_maker => 'yes',
			folders => $misc_settings,
			device => &subs::device_setter(),
			mao => eval { return decode_json $jmao } || {},
			jmao => $jmao,
			search => $search,
			resizes => $resizes,
			misses => $misses,
		);
		my $website = &Manager::window_maker({ app => 'music', title => 'music', contents => $contents }, $timestamp);
		$c->render(text => $website);
	}
	else {
		$contents = $c->render_to_string(
			crate => $crate,
			interface_colour => &subs::random_colour_grabber(),
			template => 'apps/music/music',
			layout => 'music',
			window_maker => 'no',
			folders => $misc_settings,
			device => &subs::device_setter(),
			mao => eval { return decode_json $jmao } || {},
			jmao => $jmao,
			search => $search,
			resizes => $resizes,
			misses => $misses
		);
		$c->render(text => $contents);
	}
	&subs::cache_set({ app => 'music', context => 'content' }, { contents => $contents });
};


get '/play' => sub($c) {
	my $mao = eval { return decode_json &subs::setting_grabber({ app => 'music', setting => 'mao' }) } || {};
	my $track = &subs::terminal_name(uri_decode $c->param('track'));
	my $data;

	&Manager::rock_and_roll($c);

	if ($mao->{'srv'} eq 'on') {
		Mojo::IOLoop->subprocess->run_p(sub {

			if ($device eq 'computer') {
				my $t = term_escape $track;
				`vlc $track`;
			}
			elsif ($device eq 'mobile') {
				`play $track`;
			}
		});
		$c->render(text => 'play');
	}

};

any '/music/receiver' => sub($c) {
	my $file = $c->param('file');
	my $remote_address = $c->tx->remote_address;
	my $domain = $c->param('domain');
	my $remote_uuid = $c->param('remote_uuid');
	my $toggle = $c->param('toggle');
	my $seek = $c->param('seek');
	my $volume = $c->param('volume');
	my $music_data = &subs::decrypter($c->session('suds'), decode_base64 $c->param('music_data'));
	my $piped = &subs::home('~/.president/pipe');
	my $timestamp = $c->param('timestamp');
	my $latency = $c->param('latency');
	my $queuing = $c->param('queuing');
	my $server_time = &subs::rightNow();
	my $pipe = $piped;
	my @file = split /\./, $file;
	my $ext = $file[-1];
	if ($ext eq 'enc') {
		$ext = $file[-2];
	}
	$pipe = $pipe . '.' . $ext;

	my $ws_watcher = &subs::home('~/.president/ws_watcher');
	my $pre_md = eval { return decode_json read_file($ws_watcher) } || {};

	$log->info(Dumper $pre_md);
	$log->info(Dumper $music_data);
	write_file($ws_watcher, $music_data) unless $queuing == 1;
	my $md = eval { return decode_json $music_data } || {};
	if ($toggle eq 'off') {
		`pkill play`;
		`shred -u $piped*`;
		`shred -u $ws_watcher`;
	}
	elsif ($toggle eq 'seek') {
		`pkill play`;
		$seek = $seek + ((&subs::rightNow() - $server_time) / 1000) + $latency;
		threads->create(sub() { 
			`play $pipe trim $seek 60000000000`;
		});
	}
	else {
		my $rm = &subs::db_query('select * from remote_machines where (ip=? or fqdn=?) and connection=?', $remote_address,$domain,'active')->hashes->[0];
		if ($rm->{'uuid'}) {
			if ($md->{'file'} ne $pre_md->{'file'} || $queuing == 1) {
				my $signatorial = &subs::signatorial_designer();
				$rm = &remote_useragent_maker({ ip => $rm->{'ip'}, signatorial => $signatorial, rm => $rm });

				my $url = $rm->{'manager'} . '/file_open?&file=' . uri_encode $file;
				$rm->{'ua'} = $rm->{'ua'}->max_response_size(0);
				my $res = $rm->{'ua'}->get($url);# => { Accept => '*/*' } => 'Content!');
				my $data = $res->res->content->asset->slurp;

				unless (-e $pipe) {
					`touch $pipe`;
				}
				write_file($pipe, $data);
				if ($queuing != 1) {
					`pkill play`;
					$seek = $seek + ((&subs::rightNow() - $server_time) / 1000) + $latency;
					threads->create(sub() {
						`play $pipe trim $seek 60000000000`;
					});
				}
			}
			else {
				`pkill play`;
				$seek = $seek + ((&subs::rightNow() - $server_time) / 1000) + $latency;
				threads->create(sub() {
					`play $pipe trim $seek 60000000000`;
				});
			}
		}
	}
	$c->render(json => { timestamp => $timestamp, 'seek' => $seek, file => $file, toggle => $toggle, volume => $volume, type => 'transmitter' });
};



sub music_folder_grabber() {
	my ($db,$database,$sql) = &subs::database_grabber();
	my @folders = ();
	my $settings = &Manager::misc_setting_list();
	foreach my $s ( keys %{$settings->{$device}} ) {
		if ($s =~ /_location$/gi) {
			my $status = &subs::setting_grabber({ app => 'music_folder_toggle', setting => $s, device => $device });
			push @folders, $settings->{$device}->{$s} if $status eq 'on';
		}
	}
	return @folders;
}

sub music_search($data) {
	my $search = $data->{'search'};
	my $now_playing = $data->{'now_playing'};
	my $settings = $data->{'settings'};
	my $c = $data->{'c'};
	my $misc_settings = $data->{'misc_settings'};
	my $port = $data->{'port'};
	my $misses = $data->{'misses'};
	$files = [];
	unless ($settings->{'uuid'}) {
		$settings = &subs::settings_grabber({ app => 'music' });
		$settings->{'artist'} = eval { return decode_json $settings->{'artist'} } || [];
		$settings->{'album'} = eval { return decode_json $settings->{'album'} } || [];
	}
	$permissive = 1 if $settings->{'combo_unlock'};

	my @folders;
	if (scalar @{$settings->{'album'}} > 0) { 
		push @folders, map { $_->{'path'} } @{$settings->{'album'}};
	}
	elsif (scalar @{$settings->{'artist'}} > 0) {
		push @folders, map { $_->{'path'} } @{$settings->{'artist'}};
	}
	else {
		@folders = &music_folder_grabber();
	}
	foreach my $folder (@folders) {
		$folder = &subs::home($folder);
		if (-d $folder) {
			find(\&process_file,$folder);
		}
	}
	my @local_files = @{$files};
	if ($search) {	
		@local_files = grep { lc $_ =~ /($search)/gi } @{$files};
	}
	my $crate = &song_maker({ c => $c, port => $port, files => \@local_files, settings => $settings, now_playing => $now_playing });
	$permissive = 0;
	return $crate;
}

sub song_maker($data) {
	my $files = $data->{'files'};
	my $settings = $data->{'settings'};
	my $now_playing = $data->{'now_playing'};
	my $c = $data->{'c'};
	my $misc_settings = $data->{'misc_settings'};
	my $port = $data->{'port'};
	my $config = $data->{'config'};
	my @local_files;
	my $background_colour = &subs::setting_grabber({ app => 'misc', setting => 'manager_background_colour'});
	my $computer_name = &subs::setting_grabber({ app => 'me', setting => 'computer_name' });
	my $file_informations = {};
	my ($cache_artists,$cache_albums);
	if ($data->{'settings'}-{'all_albums'} eq 'yes') {
		$cache_albums = &subs::cache_get({ app => 'music', context => 'albums' }) || {};
	}
	if ($data->{'settings'}->{'all_artists'} eq 'yes') {
		$cache_artists = &subs::cache_get({ app => 'music', context => 'artists' }) || {};
	}

	if ($data->{'same_order'} != 1) {
		if ($settings->{'shuffle'} eq 'on') {
			@local_files = shuffle(@{$files});
		}
		else {
			@local_files = sort @{$files};
		}
	}
	else {
		@local_files = @{$files};
	}
	my @lf;
	my $track_number = 0;
	my $max_files = &subs::setting_grabber({ app => 'misc', setting => 'max_files' } ) || 30;
	my $tree = &subs::inventory_grabber();
	my $artists = {};
	my $albums = {};
	my $songs = [];
	my $pushed_now_playing = 0;
	my $current_track;

	splice @local_files, $max_files;
	if ($now_playing->{'file'}) {
		my @is = grep { $_ eq $now_playing->{'file'} } @local_files;
		unshift @local_files, $now_playing->{'file'} unless grep { $_ eq $now_playing->{'file'} } @local_files;
	}

	for (my $n = 0; $n <= $max_files; $n++) {

		my $f = $local_files[$n];
		unless (-e $f) {
			next;
		}
		$track_number = $track_number + 1;
		my @t = split '/', $f;
		my @temp_path = @t;
		my $t = $t[-1];
		my $track_number = 0;
		$t =~ s/(\.mp3$|\.m4a$|\.wav$|\.flac$)//gi;
		my $extension = $1;
		my ($info,$type,@thumbs);
		if ($extension) {
			$type = 'audio';
		}
		else {
			$type = 'video';
		}
		my $filename = $f;
		my $full_title = $t;
		$tree->{lc $t} = $t[-3];
		$tree->{lc $t} .= "\n" . $t[-2] if $t[-2];
		$tree->{lc $t} .="\n" . $tree->{lc $t};
		pop @temp_path;
		my $album_folder = join '/', @temp_path;
		if ($type eq 'audio') {
			$thumb_files = [];
			find(\&process_thumbfile,$album_folder);
			@thumbs = @{$thumb_files};
		}
		my $app;
		if ($filename =~ /\.enc$/gi) {
			$app = $t[-2];
		}
		my $formatted_name = &subs::format_name(join '.', $full_title);

		pop @temp_path;
		my $artist_folder = join '/', @temp_path;

		$f = { 
			filename => $filename,
			type => $type,
			track => $t,
			extension => $extension,
			artist => $t[-3],
			artist_folder => $artist_folder,
			album => $t[-2],
			app => &subs::unformat_name($t[-2]),
			album_folder => $album_folder,
			thumbs => \@thumbs,
			info => $info,
			song => $t[-1],
			full_title => $full_title,
			formatted_name => $formatted_name,
			track_number => $track_number,
			file_uuid => &subs::random_string_creator(21),
			description => $tree->{lc $t},
			computer_name => $computer_name,
			colour => $background_colour
		};

		if ($filename && $filename !~ /\.enc$/gi) {
			if (!$file_informations->{$artist_folder}) {
				$file_informations->{$artist_folder} = &subs::cache_get({ app => 'music', context => 'file_information', subcontext => $artist_folder }) || {};
			}


			my $file_information = $file_informations->{$artist_folder};
			if ($file_information->{$filename}) {
				$f->{'info'} = $file_information->{$filename};
			}
			else {
				$f = &subs::file_media_information($f, &subs::terminal_name($filename));
				$file_information->{$filename} = $f->{'info'};
			}
			foreach my $i ( qw/artist album track genre title date composer publisher lyrics lyrics-eng/ ) {
				if ($i =~ /lyrics/ && $f->{'info'}->{'tags'}->{$i}) {
					$f->{'lyrics'} = $f->{'info'}->{'tags'}->{$i};
					$f->{'lyrics'} =~ s/\\\n|\\\r/<br>/gi;
				}
				else {
					$f->{$i} = $f->{'info'}->{'tags'}->{$i} if $f->{'info'}->{'tags'}->{$i};
				}
			}
		}
		if ($now_playing->{'file'} eq $filename) {
			$current_track = $n;
			$f->{'playing'} = 'yes';
		}
		else {

		}
		$artists->{$t[-3]}->{'full_title'} => $t[-3];
		$artists->{$t[-3]}->{'path'} = $artist_folder;
		push @{$artists->{$t[-3]}->{'tracks'}}, $f;
		$albums->{$t[-2]}->{'full_title'} => $t[-3];
		$albums->{$t[-2]}->{'path'} = $album_folder;
		push @{$albums->{$t[-3]}->{'tracks'}}, $f;

		push @{$songs}, $f;
		if ($data->{'settings'}->{'all_artists'} eq 'yes') {
			$cache_artists->{lc $f->{'artist'}} = { path => $artist_folder, artist => $f->{'artist'} };
		}
		if ($data->{'settings'}->{'all_albums'} eq 'yes') {
			$cache_albums->{lc $f->{'album'}} = { path => $album_folder, album => $f->{'album'} };
		}
	}
	foreach my $finfo ( keys %{$file_informations} ) {
		&subs::cache_set({ app => 'music', context => 'file_information', subcontext => $finfo, warranty => '-6M' }, $file_informations->{$finfo});
	}
	if ($data->{'settings'}->{'all_artists'} eq 'yes') {
		&subs::cache_set({ app => 'music', context => 'artists' }, $cache_artists);
		$artists = $cache_artists;
	}
	if ($data->{'settings'}->{'all_albums'} eq 'yes') {
		&subs::cache_set({ app => 'music', context => 'albums' }, $cache_albums);
		$albums = $cache_albums;
	}

	my $crate = { 
		config => $config, 
		remote_uuid => $c->param('remote_uuid'), 
		current_track => $current_track, 
		artist => $settings->{'artist'}, 
		album => $settings->{'album'}, 
		artists => $artists, 
		albums => $albums, 
		songs => $songs, 
		settings => $settings,
	};
	return $crate;
}

post '/music/search' => sub ($c) {
	my $title = 'music';
	my $search = lc $c->param('search');
	my $unlock = $c->param('unlock');
	my $window_maker = $c->param('window_maker');
	my $new_settings = eval { return decode_json $c->param('new_settings') } || {};
	my $resizes = $c->param('resizes');
	my $misses = eval { return decode_json $c->param('misses') } || {};
	if (!$misses->{'video'}) {
		$misses->{'video'} = &subs::random_string_creator(25,"Aa");
	}
	foreach my $ns ( keys %{$new_settings} ) {
		&subs::setting_setter({ app => 'music', setting => $ns, value => $new_settings->{$ns} });
	}


	my $port = $c->req->url->base->port;
	my $list = eval { return decode_json $c->param('list') } || [];
	my $now_playing = eval { return decode_json $c->param('now_playing') } || {};
	my $same_order = $c->param('same_order');
	&subs::cache_delete({ app => 'music', context => 'content' });
	my $artist = $c->param('artist');
	my $album = $c->param('album');
	my $misc_settings = &Manager::misc_setting_list();

	if ($unlock) {
		if (secure_compare(&subs::note_decrypter($c->session('suds'), $unlock), &subs::note_decrypter($c->session('suds'), &subs::setting_grabber({ app => 'gallery', setting => 'combo_unlock' })) ) ) {
			$permissive = 1;
		}
	}

	
	&subs::setting_setter({ app => 'music', setting => 'resizes', value => $resizes });
	&subs::setting_setter({ app => 'music', setting => 'album', value => $album });
	&subs::setting_setter({ app => 'music', setting => 'artist', value => $artist });
	&subs::setting_setter({ app => 'music', setting => 'search', value => $search });
	my $settings = &subs::settings_grabber({ app => 'music' });

	if ($settings->{'library'} ne 'local' && $c->param('remoted') ne 'yes') {
		$c->param('resizes' => undef);
		my $rm = &subs::db_query('select * from remote_machines where uuid=? and connection=?', $settings->{'library'}, 'active')->hashes->[0];
		if ($rm->{'uuid'}) {
			$c->param('remote_uuid' => $rm->{'uuid'});
			$c->param('subprocess' => 'yes');
			my $result = &Manager::remote_relay_request($c);
			if ($result =~ /music/gi) {
				my $dom = Mojo::DOM->new($result);
				$c->render(text => $result);
				return;
			}
		}
		else { &subs::setting_setter({ app => 'music', setting => 'library', value => 'local' }); $settings->{'library'} = 'local'; }
	}

	$settings->{'artist'} = eval { return decode_json $settings->{'artist'} } || [];
	$settings->{'album'} = eval { return decode_json $settings->{'album'} } || [];
	my $crate;
	if (scalar @{$list} > 0) {
		my $permissive = 1 if $settings->{'combo_unlock'};
		$crate = &song_maker({ c => $c, config => $config, port => $port, files => $list, settings => $settings, now_playing => $now_playing, same_order => $same_order, misc_settings => $misc_settings });
	}
	else {
		$crate = &music_search({ c => $c, search => $search, now_playing => $now_playing, settings => $settings, port => $port, misc_settings => $misc_settings });
	}
	
	my @interface_color= qw/yellow green blue orange black fuschia pink navy purple brown/;
	my $random_number = rand(0..8);
	my $timestamp = &subs::rightNow();
	my $jmao = &subs::setting_grabber({ app => 'music', setting => 'mao' });
	$artist = eval { return decode_json $artist } || [];
	$album = eval { return decode_json $album } || [];
	$resizes = eval { return decode_json $resizes } || {};
	my $contents;
	if ($c->param('window_maker')) {
		$contents = $c->render_to_string(
			crate => $crate,
			interface_colour => $interface_color[$random_number],
			template => 'apps/music/music',
			window_maker => 'yes',
			folders => $misc_settings,
			device => &subs::device_setter(),
			mao => eval { return decode_json $jmao } || {},
			jmao => $jmao,
			search => $search,
			resizes => $resizes,
			misses => $misses
		);
		my $website = &Manager::window_maker({ app => 'music', title => $title, contents => $contents }, $timestamp);
		$c->render(json => { artist => $artist, album => $album, search => $search, html => $website });
	}
	else {
		$contents = $c->render_to_string(
			template => 'apps/music/files',
			crate => $crate,
			search => $search,
		);
		$c->render(text => $contents);
	}
	&subs::cache_set({ app => 'music', context => 'content' }, { contents => $contents });
};

post '/music/folder_toggle' => sub($c) {
	my $timestamp = $c->param('timestamp');
	my $location = $c->param('location');
	my $status = $c->param('status');
	&subs::setting_setter({ app => 'music_folder_toggle', setting => $location, value => $status, timestamp => $timestamp });
	my $setting = &subs::setting_grabber({ app => 'music_folder_toggle', setting => $location, device => $device });
	$c->render(text => $setting);
};

post '/music/audio_output_select' => sub ($c) {
	my $output = $c->param('output');
	my $state = $c->param('state');
	my $mao = eval { return decode_json &subs::setting_grabber({ app => 'music', setting => 'mao' }) } || {};
	$mao->{$output} = $state;
	my $jmao = encode_json $mao;
	&subs::setting_setter({ app => 'music', setting => 'mao', value => $jmao });
	$c->render(json => $mao);
};

get '/music/configuration' => sub($c) {
	my $returner = {};
	my $settings = &subs::settings_grabber({ app => 'music' });
	my $jmao = $settings->{'mao'};
	$returner->{'html'} = $c->render_to_string(
		template => 'apps/music/configuration',
		folders => &Manager::misc_setting_list(),
		device => &subs::device_setter(),
		mao => eval { return decode_json $jmao } || {},
		jmao => $jmao,
		settings => $settings,
		crate => { remote_uuid => $c->param('remote_uuid') }
	);
	$c->render(json => $returner);
};

1;
