#! /usr/bin/perl
#
# Informer daemon for Vargus
###############################
#    Copyright (C) 2010-2012 Michael A. Kangin <mak@complife.ru>
#
#    Copyright: Vargus is under GNU GPL, the GNU General Public License.
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; version 2 dated June, 1991.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    http://www.gnu.org/licenses/gpl-2.0.html
#


use Getopt::Long;
use IO::Socket;
use Proc::Daemon;
use Privileges::Drop;


use Vargus::Common;
use Vargus::Objects;

our $conf_dir = "/etc/vargus";
our $stat_dir = "/var/cache/vargus";

$vargus_user = 'vargus';
our $informer_port = 9165;
$bind_address = '0.0.0.0';
$daemon_pid_file = '/var/run/vargus/_informer.pid';



sub prepare_to_die {
	die;
}

sub get_obj_num_by_name {
	my $object_type = shift;
	my $object_name = shift;

	my @objects = @{$vargus_objects{$object_type}};
	my $index = 0;
	foreach (@objects) {
		last if $objects[$index]{name} eq $object_name;
		$index++;
	}
	log_n_die  "No object with type '$object_type' and name '$object_name'" if $#objects < $index;

	return $index;
}


sub get_obj_info {
	my $object_type = shift;
	my $object_name = shift;
	my $what_info = shift;

	my $object_num;
	if ($object_name =~ /[^0-9]/) {
		$object_num = get_obj_num_by_name($object_type, $object_name);
	} else {
		my @objects = @{$vargus_objects{$object_type}};
		$object_num = $object_name - 1;
		log_n_die "Wrong index '$object_name' for '$object_type' objects" if $#objects < $object_num;

	}

	my $object = "\${\$vargus_objects{$object_type}}[$object_num]";
	
	$obj_name = eval("$object" . "{name}");
	$obj_id = eval("$object" . "{id}");

	my $query = "$object$what_info";
	my $answer = eval($query);
	$answer =~ s/\(get_port:/(find_port:/;
	$answer = expand_macroses($answer) if $do_expand_macroses;

	return "$answer";
}


sub process_query {
	my $answer = '';
	$query_object_type eq 'camera' 
		or $query_object_type eq 'set' 
		or $query_object_type eq 'view' 
		or log_n_die "Unknown query_object_type $query_object_type";

	@query_object_property or log_n_die "No query specified";
	@query_object_property = split(/,/,join(',',@query_object_property));

	if ($query_object_property[0] eq 'quantity') {
		my @objects = @{$vargus_objects{$query_object_type}};
		$answer .= ($#objects + 1 . "\n");
		shift(@query_object_property);
	}

	@query_object_name or log_n_die "No object name specified" if @query_object_property;
	@query_object_name = split(/,/,join(',',@query_object_name));

	foreach $name (@query_object_name) {
		$name or next;
		foreach $query_property (@query_object_property) {
			$property = $query_property;
			$property =~ /[^a-zA-Z0-9:]/ and s_log(LOG_WARNING, "Warning! Property $property may be not correct");
			$property =~ s/:/}{/g;
			$property =~ s/=/}[/g;
			$property = "{$property}";
			$property =~ s/(\[[0-9]+)}/\1]/g;

			$answer .= get_obj_info($query_object_type, $name, $property) . ";";
		}
		$answer =~ s/;$//g;
		$answer =~ s/,$//g;
		$answer .= "\n";
	}
	return $answer;

}

sub info_server_help {
	my $help = <<END;
	Usage:

	query TYPE;NAME-LIST;PROPERTY-LIST - get list of propertyes for list of names for object-type.
	TYPE can be: camera, view, set.
	NAME: number or name of object. Multiple NAMEs divided by commas.
	PROPERTY: multiple propertyes divided by commas.
	Example: query camera;cam1,cam2;view:preview,view:farview

	findport PORTNAME - find port with PORTNAME name.

	exit - exit.
END
	return $help;
}



sub net_daemon {

	die "Pid file already exist" if -e $daemon_pid_file;

	Proc::Daemon::Init;

	$SIG{INT} = $SIG{TERM} = $SIG{PIPE} = sub {
		if (-e $daemon_pid_file) {
			open(PIDFILE, $daemon_pid_file) or die;
			my ($test_pid) = <PIDFILE>;
			close(PIDFILE);
			unlink $daemon_pid_file if $test_pid == $$;
		}
		die;
	};

	$SIG{CHLD} = 'IGNORE';

	open(PIDFILE, "> $daemon_pid_file") or s_log(LOG_WARNING, "Can't create pid file $daemon_pid_file");
	print PIDFILE $$;
	close(PIDFILE);

	my $lsocket = IO::Socket::INET -> new(
		LocalPort => $informer_port,
		Listen => 5,
		Proto => 'tcp',
		Bind => $bind_address,
		ReuseAddr => 1,
		Timeout => 5,
	) or log_n_die "Can't create socket";

	while (1) {
		next unless my $connection = $lsocket->accept;
		defined (my $pid = fork()) or log_n_die "Can't fork new child: $!";
		unless ($pid) {
			$lsocket->close;
			serve_client($connection);
			exit 0;
		}
		$connection->close;
	}
}

sub serve_client {
	my $sock = shift;

	print $sock ": Vagrus informer. Enter your query or \"help\" for help.\n";
	print $sock ">\n";

	while (<$sock>) {
		chomp;
		s/\r*$//;
		$_ or next; 
#		s_log(LOG_DEBUG, "Get query: $_");
		if (/^(exit|quit)$/) {
			print $sock ": Bye.\n";
			last;
		} elsif (/^help$/) {
			print $sock info_server_help;
		} elsif (/^query($|\s)/) {
			my (undef,$query_string) = split(' ', $_, 2);
			$do_expand_macroses = $query_string =~ s/^!expand //;
			$do_expand_macroses = !$do_expand_macroses;
			my ($q_type, $q_name, $q_prop) = split(';', $query_string);
			unless ($query_object_type = $q_type) {
				print $sock ": No TYPE info. See 'help' for details\n";
				next;
			}

			unless ($query_object_type =~ /camera|view|set/) {
				print $sock ": Wrong query TYPE. See 'help' for details\n";
				next;
			}

			if ($q_name eq 'quantity' and ! $q_prop) {
				$q_prop = $q_name;
				$q_name = '';
			}

			@query_object_name = split(',', $q_name);
			@query_object_property = split(',', $q_prop);
			
			unless (@query_object_property) {
				print $sock ": No PROPERTY for query. See 'help' for details\n";
				next;
			}
			print $sock process_query;

		} elsif (/^findport($|\s)/) {
			my (undef, $port) = split(' ', $_);
			unless ($port) {
				print $sock ": Bad value $port for findport command\n";
				next;
			}
			print $sock expand_macroses("(find_port:$port)") . "\n";
		} else {
			print $sock ": Unknown command. See 'help' for details\n";
		}
	}
	continue {
		print $sock ">\n";
	}
}



($new_uid, $new_gid, @new_gids) = drop_privileges($vargus_user) or log_n_die("Could not drop privileges") if not $>;

$obj_name = "";
$obj_id = "";

init_objects;

our $query_object_type;
our @query_object_name;
our @query_object_property;
our $damon_mode;
our $do_expand_macroses = 1;

GetOptions(     't=s' => \$query_object_type,
                'n=s' => \@query_object_name,
                'q=s' => \@query_object_property,
		'expand!' => \$do_expand_macroses,
		'daemon!' => \$daemon_mode
);

net_daemon if $daemon_mode;

print(process_query);
