#!/usr/bin/perl
#
# $Name$
#
# $Log$

use warnings;
use strict;

use AppConfig qw( :argcount :expand );
use Math::NumberCruncher;
use DBI;
use IO;
use Time::HiRes qw( tv_interval gettimeofday );

STDOUT->autoflush;

sub setup_args( $$ );
sub calc_color_terms( $$ );
sub calc_zero_points( $$ );

use constant ID => q$Id: mergeStars.pl,v 1.5 2002/02/20 14:08:30 robc Exp $;

my $version = join ( ' ', ( split ( ' ', ID ) )[ 1 .. 3 ] );
$version =~ s/,v\b//;
$version =~ s/(\S+)$/($1)/;

my $CONFIG_FILE = $ENV{ TASSIV_CFG }
   || '/tass/src/tassiv_reduce/tassiv_reduce.cfg';
my $c = AppConfig->new(
   {
      CREATE => 1,
      GLOBAL => {
                  EXPAND   => EXPAND_VAR,
                  ARGCOUNT => ARGCOUNT_ONE
      }
   }
);
$c = setup_args( $c, $CONFIG_FILE );

my $DB_NAME = $c->name;
my $DB_HOST = $c->host;
my $group_id;

my $dbh =
   DBI->connect( "DBI:Pg:dbname=$DB_NAME;host=$DB_HOST", "", "",
                 { RaiseError => 1, AutoCommit => 0 } );

#
# Temp table
$dbh->do( qq/
SELECT                    ref.star_id,
       v.imag          AS v_mag,
       i.imag          AS i_mag,
       v.obs_id        AS v_obs_id,
       i.obs_id        AS i_obs_id,
       v.pair_id       AS pair_id,
       v.group_id      AS group_id
INTO TEMP color
FROM reference_ubvri AS ref,
   (SELECT star_id, imag, pair_id, group_id, file_id, obs_id
    FROM obs_v NATURAL JOIN files
    WHERE  star_id < 10000000
       AND smag < 1
       AND x BETWEEN / . $c->x_start . qq/ AND / . $c->x_end . qq/
       AND y BETWEEN / . $c->y_start . qq/ AND / . $c->y_end . qq/
   ) AS v,
   (SELECT star_id, imag, pair_id, group_id, file_id, obs_id
    FROM obs_i NATURAL JOIN files
    WHERE star_id < 10000000
       AND smag < 1
       AND x BETWEEN / . $c->x_start . qq/ AND / . $c->x_end . qq/
       AND y BETWEEN / . $c->y_start . qq/ AND / . $c->y_end . qq/
   ) AS i
WHERE  v.group_id = i.group_id
	AND NOT EXISTS (SELECT *
	                FROM color_group AS cg
			WHERE cg.group_id = v.group_id)
   AND v.pair_id  = i.pair_id
   AND v.star_id  = i.star_id
   AND ref.star_id = v.star_id
   AND ref.v BETWEEN / . $c->mag_max . qq / AND / . $c->mag_min . qq/
   AND ref.i BETWEEN / . $c->mag_max . qq / AND / . $c->mag_min . qq//
);

printf "Found %d stars to use for color calculations...",
   $dbh->selectrow_array( 'SELECT count(*) FROM color' );

#
# Temp table
$dbh->do( qq/
SELECT            pair_id,
                  group_id,
       0::real AS v_zero_point,
       0::real AS i_zero_point
INTO TEMP color_zero_point
FROM color
GROUP BY group_id, pair_id/
);

printf "\nAcross %d frames...",
   $dbh->selectrow_array( 'SELECT count(*) FROM color_zero_point' );

#
# Temp table
$dbh->do( qq/
SELECT 0::real AS v_color,
       0::real AS i_color,
                  group_id
INTO TEMP color_terms
FROM color_zero_point
GROUP BY group_id/
);

printf "\nAnd %d nights...\n",
   $dbh->selectrow_array( 'SELECT count(*) FROM color_terms' );

my $sth_select_group_id = $dbh->prepare( qq/
SELECT DISTINCT group_id FROM color ORDER BY group_id/
);

$sth_select_group_id->execute;

while ( $group_id = $sth_select_group_id->fetchrow_array )
   {
   my $max_zp = 100;
   my $last_max_zp;
   my $count = 0;

   do
      {
      $last_max_zp = $max_zp;
      calc_color_terms( $dbh, $group_id );
      $max_zp = calc_zero_points( $dbh, $group_id );
      printf "%2d - %4.2e\n", ++$count, $max_zp;
      }
   while (  ( $max_zp > 1e-10 )
         && ( ( $last_max_zp - $max_zp ) > 0 )
         && ( ( $last_max_zp - $max_zp ) > 1e-10 )
         && ( $count < $c->max_iter ) );

   print "Marking stars using to solve for color/zero point...";

   $dbh->do( qq/
UPDATE obs_v
SET solve = true
FROM color
WHERE obs_v.obs_id = color.v_obs_id
   AND color.group_id = $group_id/
   );

   $dbh->do( qq/
UPDATE obs_i
SET solve = true
FROM color
WHERE obs_i.obs_id = color.i_obs_id
   AND color.group_id = $group_id/
   );

   $dbh->do( qq/
INSERT INTO color_group( group_id, color_v, color_i )
   SELECT group_id, v_color, i_color
   FROM color_terms
   WHERE group_id = $group_id/
   );

   $dbh->do( qq/DELETE FROM zero_pair
             WHERE EXISTS (SELECT pair_id
                              FROM zero_pair NATURAL JOIN files
                              WHERE group_id = $group_id)/
   );

   $dbh->do( qq/
INSERT INTO zero_pair( pair_id, zero_v, zero_i )
   SELECT pair_id, v_zero_point, i_zero_point
	FROM color_zero_point
	WHERE group_id = $group_id/
   );

   my $night
      = $dbh->selectrow_array(
           "SELECT night FROM groups WHERE group_id = $group_id" );
   print "\nUpdating magnitudes - night $night ($group_id), V color...";

   my $t0 = [ gettimeofday ];
   $dbh->do( qq/
UPDATE obs_v
SET mag = obs_v.imag + zp.zero_v + cg.color_v * (obs_v.imag - i.imag),
    use = true
FROM color_group AS cg, zero_pair AS zp, obs_i AS i, files AS f
WHERE  obs_v.star_id = i.star_id
   AND obs_v.file_id = f.file_id
   AND cg.group_id = f.group_id
   AND f.group_id = $group_id
   AND zp.pair_id = f.pair_id/
   );
   my $elapsed = tv_interval( $t0, [ gettimeofday ] );
   printf "%.0f seconds\n", $elapsed;

   print "Updating magnitudes - night $night ($group_id), I color...";

   $t0 = [ gettimeofday ];
   $dbh->do( qq/
UPDATE obs_i
SET mag = obs_i.imag + zp.zero_i + cg.color_i * (v.imag - obs_i.imag),
    use = true
FROM color_group AS cg, zero_pair AS zp, obs_v AS v, files AS f
WHERE  obs_i.star_id = v.star_id
   AND obs_i.file_id = f.file_id
   AND cg.group_id = f.group_id
   AND f.group_id = $group_id
   AND zp.pair_id = f.pair_id/
   );
   $elapsed = tv_interval( $t0, [ gettimeofday ] );
   printf "%.0f seconds\n", $elapsed;

   $dbh->commit;
   }

$dbh->disconnect;

#system( qw/psql -d tassiv -f color_g.sql/ );
#system( qw/gnuplot color.dem/ );

exit;

sub calc_color_terms( $$ )
   {
   my ( $dbh, $group_id ) = @_;
   my ( @v_diff, @i_diff, @inst_color, $v_color, $i_color, $v_temp, $i_temp );

   $dbh->do( qq/SELECT                         star_id,
                       v_mag + v_zero_point AS v_zmag,
                       i_mag + i_zero_point AS i_zmag,
                       0::real              AS inst_color
                INTO TEMP color_temp
                FROM color NATURAL JOIN color_zero_point
                WHERE color.group_id = $group_id/
   );
   $dbh->do( qq/UPDATE color_temp
                SET    inst_color = v_zmag - i_zmag/
   );

   my $sth_select = $dbh->prepare_cached( qq/SELECT v_zmag - v AS v_diff,
                                                    i_zmag - i AS i_diff,
                                                    inst_color
                               FROM color_temp NATURAL JOIN reference_ubvri/
   );
   $sth_select->execute;

   my %select;
   $sth_select->bind_columns( \( @select{ @{ $sth_select->{ NAME_lc } } } ) );
   while ( $sth_select->fetch )
      {
      push @v_diff,     $select{ v_diff };
      push @i_diff,     $select{ i_diff };
      push @inst_color, $select{ inst_color };
      }
   ( $v_color, $v_temp ) =
      Math::NumberCruncher::BestFit( \@inst_color, \@v_diff );
   ( $i_color, $i_temp ) =
      Math::NumberCruncher::BestFit( \@inst_color, \@i_diff );
   $dbh->do( qq/UPDATE color_terms
                SET v_color = $v_color,
                    i_color = $i_color/
   );

   {
      local $dbh->{ RaiseError };
      local $dbh->{ PrintError };
      $dbh->do( qq/DROP TABLE color_temp/ );
   }
   }

sub calc_zero_points( $$ )
   {
   my ( $dbh, $group_id ) = @_;

   $dbh->do( qq/SELECT            star_id,
                                  pair_id,
                      v_mag + v_zero_point + v_color*(v_mag - i_mag) AS v_cmag,
                      i_mag + i_zero_point + i_color*(v_mag - i_mag) AS i_cmag
                INTO TEMP color_temp
                FROM color NATURAL JOIN color_zero_point, color_terms/
   );

   my $sth_select =
      $dbh->prepare_cached( qq/SELECT avg( v_cmag - v ) AS v_zero,
                                      avg( i_cmag - i ) AS i_zero,
                                      pair_id
                               FROM color_temp NATURAL JOIN reference_ubvri
                               GROUP BY pair_id/
   );
   my $sth_update = $dbh->prepare_cached( qq/UPDATE color_zero_point
                               SET v_zero_point = v_zero_point + ?,
                                   i_zero_point = i_zero_point + ?
                               WHERE pair_id = ?/
   );

   $sth_select->execute;

   my %select;
   my $max = 0;

   $sth_select->bind_columns( \( @select{ @{ $sth_select->{ NAME_lc } } } ) );
   while ( $sth_select->fetch )
      {
      $sth_update->execute(
                            -$select{ v_zero }, -$select{ i_zero },
                            $select{ pair_id }
      );
      if ( abs( $select{ v_zero } ) > $max )
         {
         $max = abs( $select{ v_zero } );
         }
      if ( abs( $select{ i_zero } ) > $max )
         {
         $max = abs( $select{ i_zero } );
         }
      }

   {
      local $dbh->{ RaiseError };
      local $dbh->{ PrintError };
      $dbh->do( qq/DROP TABLE color_temp/ );
   }

   return $max;
   }

sub setup_args( $$ )
   {
   my ( $c, $file ) = @_;

   $c->define(
               Color_x_start => {
                                  DEFAULT => '200',
                                  ALIAS   => 'x_start'
               },
               Color_x_end => {
                                DEFAULT => '1800',
                                ALIAS   => 'x_end'
               },
               Color_y_start => {
                                  DEFAULT => '200',
                                  ALIAS   => 'y_start'
               },
               Color_y_end => {
                                DEFAULT => '1800',
                                ALIAS   => 'y_end'
               },
               Color_mag_max => {
                                  DEFAULT => '8',
                                  ALIAS   => 'mag_max'
               },
               Color_mag_min => {
                                  DEFAULT => '10',
                                  ALIAS   => 'mag_min'
               },
               Color_max_iter => {
                                   DEFAULT => '10',
                                   ALIAS   => 'max_iter'
               },
               DB_name => {
                            DEFAULT => 'tassiv',
                            ALIAS   => 'name'
               },
               DB_host => {
                            DEFAULT => 'localhost',
                            ALIAS   => 'host'
               }
   );
   $c->file( $file );

   $c->args( \@ARGV );

   return $c;
   }
