#!/usr/bin/perl
use strict;
use warnings;

use SKM::DB;
use Data::Dumper;
use JSON;

# CONS
my $APL = $ENV{APL};
my $VAE_LIST = "$APL/conf/bin/cartridge-ctl type=vae query=list";
my %VIS_MAP = (
    "disable" => 0,
    "alerts" => 1,
    "tracks" => 2,
    "rules" => 3
);
my $DEF_VIS = 0;
my $MIN_FPS = 4; # Set MIN_FPS to 4 by default
my $OV5_LICENSE = "/opt/vae-ov/bin/ov5_license";

# VARS
my $dbm;
my %dbs;
my %vae_list;
my %Cams;

# SUBS

sub logger {
    my $msg = shift;
    chomp $msg;
    print "$msg\n";
}

sub list_vae {
    %vae_list = map {/^(\w+)=(.*)$/} grep {/^\w+=/} `$VAE_LIST`;
    die "Error fetching vae list" if $?;
}

sub db_master {
    eval {
        $dbm = DBMaster({RaiseError => 1, PrintError => 0, AutoCommit => 0});
        $dbs{UPDATE_ATTR} = $dbm->prepare("UPDATE _obj_attr SET val=? WHERE obj=? AND attr=?");
        $dbs{CLEAR_RTIME}=$dbm->prepare("UPDATE _objs SET rtime=null WHERE obj=?");
    };
    die "DB connection failed: $@" if $@;
}

sub fetch_attributes {
    my $ra;
    eval {
        $ra = $dbm->selectall_arrayref(
            "SELECT o.obj,a.attr,a.val FROM _objs o INNER JOIN _obj_attr a 
            ON o.obj=a.obj WHERE o.deleted=0 AND o.otype='D' and o.subtype='C'"
        );
    };
    die "Fetch attributes error: $@" if $@;

    foreach my $row (@$ra) {
        my ($obj, $attr, $val) = @$row;
        next if not $obj or not $attr;
        $Cams{$obj}{$attr} = $val;
    }
}

sub migrate {
    for my $obj (keys %Cams) {
        my $cam = $Cams{$obj};
        next if not $cam->{ANALYTICS_CFG};
        next if not $cam->{ANALYTICS_SCHEDULER};
        
        my $vae_active = $cam->{ANALYTICS} eq 'on' ? 'yes' : 'no';
        my $anl_ov = $cam->{ANALYTICS_OV};
        my $anl_ovc = $cam->{ANALYTICS_OV_COUNTING};
        my $anl_vca = $cam->{ANALYTICS_VCA};
        my $vis = $VIS_MAP{ $cam->{ANALYTICS_VISUALIZATION} } || $DEF_VIS;
        my $anl_cfg = $cam->{ANALYTICS_CFG};
        my $anl_sched = $cam->{ANALYTICS_SCHEDULER};
        my $skipframes = $cam->{TRACKER_SKIPFRAMES};
        my $json_modified = 0;
        my $ov_tier = "basic";
        
        my %engines = ();
        $engines{ov} = 1 if $anl_ov eq 'on' or $anl_ovc eq 'on';
        $engines{vca} = 1 if $anl_vca eq 'on';
        
        # Determine which engine can be activated for current camera
        my $ov_active = 1 if $engines{ov} and $vae_list{ov};
        my $vca_active = 1 if $engines{vca} and $vae_list{vca};
        
        # Noting to migrate currently
        next if !$ov_active && !$vca_active;
        
        my %vae_cfg = ( engines => {} );
        my $vae_config = $cam->{ANALYTICS_CFG};
        
        # Compute MIN_FPS and MAX_FPS for trackers 
        # according to TRACKER_SKIPFRAMES legacy attribute value
        my $max_fps = int(30 / ($skipframes + 1));
        my $min_fps = 4;
        $max_fps = 30 if $max_fps > 30;
        $max_fps = 10 if $max_fps <= $min_fps;
        
        foreach my $vae (keys %vae_list) {
            $vae_cfg{engines}{$vae} = {
                ACTIVE => $engines{$vae} ? "yes" : "no",
                MIN_FPS => $min_fps,
                MAX_FPS => $max_fps
            };
        }
        my $vae_cfg_json = encode_json(\%vae_cfg);

        
        # Construct VAE_{vae}_SCHEDULER attribute
        # Must add new elements to existing json
        #
        my $cfg_json = decode_json($anl_cfg);
        my $sched_json = decode_json($anl_sched);
        my $zone_set = $cfg_json->{root}{zone_set};
        my $i = 0;
        foreach my $rule (@$sched_json) {
            $rule->{postures} = [];
            $rule->{state} = $rule->{active} ? "active" : "non-active";
            $rule->{name} = $zone_set->[$i]{"name"};
            $i++;
        }
        
        my $vae_sched = encode_json($sched_json);
        
        # Fix VAE_${vae}_CONFIG content due to format changes in
        # 'minimum' and 'maximum' filters
        my $vae_config_json = decode_json($vae_config);
        my $event_set = $vae_config_json->{root}{event_set};
        my $MAX_WIDTH = 65535;
        my $MAX_HEIGHT = 65535;
        
        if ($event_set and ref $event_set eq 'ARRAY') {
            foreach my $rule (@$event_set) {
                my $type = $rule->{rule_type};
                next if not $type or ref($type) ne 'HASH';
                next if not $type->{minimum} and not $type->{maximum};
                $json_modified = 1;
                my $min = $type->{minimum};
                my $max = $type->{maximum};
                if ($min) {
                    my $near_w = $min->{min_width_near};
                    my $near_h = $min->{min_height_near};
                    my $far_w = $min->{min_width_far};
                    my $far_h = $min->{min_height_far};
                    # convert % to integers in range [1 .. 65535]
                    my $near_w_conv = int ( ($MAX_WIDTH * $near_w) / 100 );
                    my $near_h_conv = int ( ($MAX_HEIGHT * $near_h) / 100 );
                    my $far_w_conv = int ( ($MAX_WIDTH * $far_w) / 100 );
                    my $far_h_conv = int ( ($MAX_HEIGHT * $far_h) / 100 );
                    
                    $min->{near_width} = $near_w_conv;
                    $min->{near_height} = $near_h_conv;
                    $min->{far_width} = $far_w_conv;
                    $min->{far_height} = $far_h_conv;
                    $min->{near_x} = 100;
                    $min->{near_y} = 100;
                    $min->{far_x} = 300;
                    $min->{far_y} = 300;
                    
                    # Delete old values
                    delete $min->{min_width_near};
                    delete $min->{min_height_near};
                    delete $min->{min_width_far};
                    delete $min->{min_height_far};
                }
                elsif ($max) {
                    my $near_w = $max->{max_width_near};
                    my $near_h = $max->{max_height_near};
                    my $far_w = $max->{max_width_far};
                    my $far_h = $max->{max_height_far};
                    # convert % to integers in range [1 .. 65535]
                    my $near_w_conv = int ( ($MAX_WIDTH * $near_w) / 100 );
                    my $near_h_conv = int ( ($MAX_HEIGHT * $near_h) / 100 );
                    my $far_w_conv = int ( ($MAX_WIDTH * $far_w) / 100 );
                    my $far_h_conv = int ( ($MAX_HEIGHT * $far_h) / 100 );
                    
                    $max->{near_width} = $near_w_conv;
                    $max->{near_height} = $near_h_conv;
                    $max->{far_width} = $far_w_conv;
                    $max->{far_height} = $far_h_conv;
                    $max->{near_x} = 100;
                    $max->{near_y} = 100;
                    $max->{far_x} = 300;
                    $max->{far_y} = 300;
                    
                    # Delete old values
                    delete $max->{max_width_near};
                    delete $max->{max_height_near};
                    delete $max->{max_width_far};
                    delete $max->{max_height_far};

                }
            }
            
            # Finally, encode back mofified structure to json string
            $vae_config = encode_json($vae_config_json) if $json_modified;
        }
        
        # Set VAE_OV_TIER attribute for correct license checking
        if ($ov_active and -x $OV5_LICENSE) {
            $ov_tier = `echo '$vae_config' | $OV5_LICENSE`;
            chomp $ov_tier;
            $ov_tier = "basic" unless $ov_tier;
        }
        
        eval {
            $dbs{UPDATE_ATTR}->execute($vae_cfg_json, $obj, "VAE_CONFIG");
            $dbs{UPDATE_ATTR}->execute($vae_active, $obj, "VAE_ACTIVE");
            $dbs{UPDATE_ATTR}->execute($vis, $obj, "VAE_VISUALIZATION");
            
            if ($ov_active) {
                $dbs{UPDATE_ATTR}->execute("yes", $obj, "VAE_OV_ACTIVE");
                $dbs{UPDATE_ATTR}->execute($vis, $obj, "VAE_OV_VISUALIZATION");
                $dbs{UPDATE_ATTR}->execute($vae_config, $obj, "VAE_OV_CONFIG");
                $dbs{UPDATE_ATTR}->execute($vae_sched, $obj, "VAE_OV_SCHEDULER");
                $dbs{UPDATE_ATTR}->execute($min_fps, $obj, "VAE_OV_MIN_FPS");
                $dbs{UPDATE_ATTR}->execute($max_fps, $obj, "VAE_OV_MAX_FPS");
                $dbs{UPDATE_ATTR}->execute($ov_tier, $obj, "VAE_OV_TIER");
            }
            else {
                $dbs{UPDATE_ATTR}->execute("yes", $obj, "VAE_VCA_ACTIVE");
                $dbs{UPDATE_ATTR}->execute($vis, $obj, "VAE_VCA_VISUALIZATION");
                $dbs{UPDATE_ATTR}->execute($vae_config, $obj, "VAE_VCA_CONFIG");
                $dbs{UPDATE_ATTR}->execute($vae_sched, $obj, "VAE_VCA_SCHEDULER");
                $dbs{UPDATE_ATTR}->execute($min_fps, $obj, "VAE_VCA_MIN_FPS");
                $dbs{UPDATE_ATTR}->execute($max_fps, $obj, "VAE_VCA_MAX_FPS");
            }
            
            # Reset rtime
            $dbs{CLEAR_RTIME}->execute($obj);
            
            $dbm->commit;
        };
        if ($@) {
            my $err = $@;
            eval { $dbm->rollback } if $dbm;
            die "Migration failed (obj=$obj): $err\n";
        }
        
        logger("OBJ=$obj OV=$anl_ov VCA=$anl_vca MAX_FPS=$max_fps VIS=$vis MOD=$json_modified");
    }
}

sub main {

    list_vae;

    db_master;

    fetch_attributes;
    
    migrate;
}

# MAIN
main;

END {
    eval { $dbm->disconnect } if $dbm;
}


# TODO: Combine current value of VAE_CONFIG with migrated one
