package MainWindow;

use inversionsrechnung_fit_LNVT;
use inversionsrechnung_Phillips_Twomey;
use inversionsrechnung;
use ResultWindow;
use ResultGraph;
use AttenuationGraph;
if ($^O=~'Win32') {
  require RecordsetSelector; }

use strict;
use warnings;
use vars qw(@ISA);

@ISA=qw(Wx::Frame);

use Wx qw(:everything);
use Wx::Event qw(EVT_MENU EVT_CLOSE EVT_BUTTON EVT_END_PROCESS EVT_IDLE EVT_TIMER);
use Wx::Grid;
use Wx::Locale;
use PDL;
use PDL::Core;
use File::Spec; # for OS-agnostic file and path names
use File::Basename;

use Carp;
my $Debugging = 0;


my $inversion_script = File::Spec->rel2abs(File::Spec->catfile(File::Spec->curdir(),'alpha_schaetz_LNVT_Faran_PGV_calc_frontend.pl'));

use lib qw( Module );
use Faran_lsg_pdl;

my( $ID_ABOUT, $ID_EXIT,
    $Load_attenuation_Btn_ID,
    $Load_particle_props_Btn_ID,
    $Load_fluid_props_Btn_ID,
    $Display_graph_Btn_ID,
    $Display_table_Btn_ID,
    $Display_attn_Graph_Btn_ID,
    $fit_process_ID,
    $ID_LOAD_DB) = ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

my $attenuation_file = File::Spec->catfile('Examples','Glas_H2O_40-50_2.5Ma.txt');
my $particle_file = File::Spec->catfile('Examples','ballotini_elastic.txt');
my $fluid_file = File::Spec->catfile('Examples','wasser.txt');

# Umrechnung Np/m/MHz -> dB/cm/MHz
my $dB = 20/log(10) / 100;

# Parameters: title, position, size
sub new {
    my( $class ) = shift;
    # now we are calling the base class constructor
    my( $this ) = $class->SUPER::new( undef, -1, $_[0], $_[1], $_[2] );

    # now that you have an object you can store some data inside of it
    $this->{inversion} = inversionsrechnung->new($this);
    #$this->{particle_properties} = material->new($this);
    #$this->{fluid_properties} = material->new($this);
    #$this->{attenuation_measurement} = attenuation->new($this);

    # create 2 menus
    my( $mfile ) = Wx::Menu->new();
    my( $mhelp ) = Wx::Menu->new();

    if ($^O=~'Win32') {
      # add entries to menus
      $mfile->Append( $ID_LOAD_DB, "Select &Database\tAlt-D", "Select the database");
      # Connect Event
      EVT_MENU( $this, $ID_LOAD_DB, \&OnSelectDatabase );
      # Create a Dialog that can be used for record selection from the DT database
      $this->{rec_set}=RecordsetSelector->new($this,-1,
                "Select recordsets from DT database",
                 wxDefaultPosition,
                [300,240],
                wxDEFAULT_DIALOG_STYLE);
    }
    # add entries to menus
    $mhelp->Append( $ID_ABOUT, "&About...\tCtrl-A", "Show about dialog" );
    $mfile->Append( $ID_EXIT, "E&xit\tAlt-X", "Quit this program" );

    # create a menu bar
    my( $mbar ) = Wx::MenuBar->new();

    # append the menus to the menu bar
    $mbar->Append( $mfile, "&File" );
    $mbar->Append( $mhelp, "&Help" );

    # and assign the menubar to the frame
    $this->SetMenuBar( $mbar );

    # declare that events coming from menu items with the given
    # id will be handled by the frame ($this) using the listed methods

    # in wxWindows you will have used EVT_MENU( id, method )
    # with _two_ parameters, and EVT_CLOSE( method ), with _one_ parameter
    EVT_MENU( $this, $ID_EXIT, \&OnQuit );
    EVT_MENU( $this, $ID_ABOUT, \&OnAbout );
    EVT_CLOSE( $this, \&OnClose );
    # Add a panel for better optics
    $this->{toppanel} = Wx::Panel->new($this,-1);
    # create a status bar with two fields
    $this->CreateStatusBar( 2 );
    # and show a friendly message
    $this->SetStatusText( "MVT-TUD US Analyzer", 1 );

    # Create the layout: top sizer
    $this->{toppanel}->{topsizer} = Wx::BoxSizer->new(wxVERTICAL );
    #------------------------
    # tell we want automatic layout
    $this->{toppanel}->SetAutoLayout(1);
    #------------------------
    # Sizer for measurement values
    my $box=Wx::StaticBox->new(
                   $this->{toppanel},
                   -1,
                   "Attenuation",Wx::Point->new(-1,-1),Wx::Size->new(100,100));
    my ($attenuation_sizer) = Wx::StaticBoxSizer->new($box,wxHORIZONTAL);

    # Create a grid
    #my ($grid) = Wx::Grid->new( $this,
    $this->{grid} = Wx::Grid->new( $this->{toppanel},
                        -1,
                        wxDefaultPosition,
                        Wx::Size->new(400,70));
    # Adjust number of Rows/Columns in grid
    $this->{grid}->CreateGrid( 1, 17 );
    # Set Row Label
    $this->{grid}->SetRowLabelValue(0,"alpha [Db/cm/MHz]");
    $this->{grid}->SetRowLabelSize(120);
    # Set grid sizes
    $this->{grid}->AutoSizeColumns(1);
    # Add grid to grid sizer
    $attenuation_sizer->Add( $this->{grid},
                  1,
                  wxEXPAND|wxALL,
                  10);
    # Create a Button (test)
    # store the id of the button in $BTNID
    my ($load_attn_file_Btn) = Wx::Button->new( $this->{toppanel},
                 $Load_attenuation_Btn_ID,
                 "Load attenuation data",
                 wxDefaultPosition,
                 Wx::Size->new(130,30));
    $attenuation_sizer->Add($load_attn_file_Btn,
                  0, # make horizontally unstretchable
                  wxADJUST_MINSIZE|wxALL, # make border all around (implicit top alignment)
                  10 ); # set border width to 10
    # Add grid sizer to main sizer
    $this->{toppanel}->{topsizer}->Add($attenuation_sizer,
                  1,
                  wxEXPAND|wxALL,
                  10);
    #------------------------

    # Sizer for material properties
    my $box_mat_prop=Wx::StaticBox->new(
                   $this->{toppanel},
                   -1,
                   "Material Properties",Wx::Point->new(-1,-1),Wx::Size->new(100,100));
    my ($mat_prop_sizer) = Wx::StaticBoxSizer->new($box_mat_prop,wxVERTICAL);

    $this->{toppanel}->{topsizer}->Add($mat_prop_sizer,
                  0,
                  wxEXPAND|wxALL,
                  10);

    #------------------------
    # Sizer for particle material properties
    my ($particle_mat_prop_sizer) = Wx::StaticBoxSizer->new(Wx::StaticBox->new(
                   $this->{toppanel},
                   -1,
                   "Particle"),
                  wxHORIZONTAL);
    $mat_prop_sizer->Add($particle_mat_prop_sizer,
                   1,
                   wxEXPAND|wxALL,
                   5);

    # The above sizer contains sizers for each material property:
    ###############
    # Longitudinal sound velcoity
    my ($particle_cL_sizer) = Wx::BoxSizer->new( wxHORIZONTAL );
    $particle_mat_prop_sizer->Add($particle_cL_sizer,
                   1,
                   wxEXPAND|wxALL,
                   0);
    # Define Label of c_P,L:
    $particle_cL_sizer->Add(Wx::StaticText->new($this->{toppanel},
                   -1,
                   "c_P,L",
                   wxDefaultPosition,
                   Wx::Size->new(-1,-1),
                   wxALIGN_RIGHT),
                   1,
                   wxEXPAND|wxALL,
                   5);
    # Define Control for c_P,L
    $this->{c_P_L_Control} = Wx::TextCtrl->new($this->{toppanel},
                   -1, # id
                   wxTE_RIGHT|wxTE_PROCESS_ENTER);
    $particle_cL_sizer->Add($this->{c_P_L_Control},
                   1,
                   wxEXPAND|wxALL,
                   5);
    # Define unit label
    $particle_cL_sizer->Add(Wx::StaticText->new($this->{toppanel},
                   -1,
                   "m/s"),
                   1,
                   wxEXPAND|wxALL,
                   5);
    #############
    # Transversal sound velcoity
    my ($particle_cT_sizer) = Wx::BoxSizer->new( wxHORIZONTAL );
    $particle_mat_prop_sizer->Add($particle_cT_sizer,
                   1,
                   wxEXPAND|wxALL,
                   0);
    # Define Label of c_P,T:
    $particle_cT_sizer->Add(Wx::StaticText->new($this->{toppanel},
                   -1,
                   "c_P,T",
                   wxDefaultPosition,
                   Wx::Size->new(-1,-1),
                   wxALIGN_RIGHT),
                   1,
                   wxEXPAND|wxALL,
                   5);
    # Define Control for c_P,T
    $this->{c_P_T_Control} = Wx::TextCtrl->new($this->{toppanel},
                   -1, # id
                   wxTE_RIGHT|wxTE_PROCESS_ENTER);
    $particle_cT_sizer->Add($this->{c_P_T_Control},
                   1,
                   wxEXPAND|wxALL,
                   5);
    # Define unit label
    $particle_cT_sizer->Add(Wx::StaticText->new($this->{toppanel},
                   -1,
                   "m/s"),
                   1,
                   wxEXPAND|wxALL,
                   5);
    #########

    #############
    # Particle density
    my ($particle_rho_sizer) = Wx::BoxSizer->new( wxHORIZONTAL );
    $particle_mat_prop_sizer->Add($particle_rho_sizer,
                   1,
                   wxEXPAND|wxALL,
                   0);
    # Define Label of rho_P:
    $particle_rho_sizer->Add(Wx::StaticText->new($this->{toppanel},
                   -1,
                   "rho_P",
                   wxDefaultPosition,
                   Wx::Size->new(-1,-1),
                   wxALIGN_RIGHT),
                   1,
                   wxEXPAND|wxALL,
                   5);
    # Define Control for rho_P
    $this->{P_rho_Control} = Wx::TextCtrl->new($this->{toppanel},
                   -1, # id
                   wxTE_RIGHT|wxTE_PROCESS_ENTER);
    $particle_rho_sizer->Add($this->{P_rho_Control},
                   1,
                   wxEXPAND|wxALL,
                   5);
    # Define unit label
    $particle_rho_sizer->Add(Wx::StaticText->new($this->{toppanel},
                   -1,
                   "kg/m³"),
                   1,
                   wxEXPAND|wxALL,
                   5);
    #########

    # Create a "Load Material Properties"-Button for Particles button
    my ($Load_Particle_Material_Btn) =Wx::Button->new( $this->{toppanel},
                 $Load_particle_props_Btn_ID,
                 "Load Material Properties" );
    # Add button to sizer
    $particle_mat_prop_sizer->Add($Load_Particle_Material_Btn,
                  0, # make horizontally unstretchable
                  wxEXPAND|wxALL, # make border all around (implicit top alignment)
                  5 ); # set border width to 5
    #############
    #------------------------
    # Sizer for fluid material properties
    my ($fluid_mat_prop_sizer) = Wx::StaticBoxSizer->new(Wx::StaticBox->new(
                   $this->{toppanel},
                   -1,
                   "Fluid"),
                  wxHORIZONTAL);
    $mat_prop_sizer->Add($fluid_mat_prop_sizer,
                   1,
                   wxEXPAND|wxALL,
                   5);

    # The above sizer contains sizers for each material property:
    ###############
    # Longitudinal sound velcoity
    my ($fluid_cL_sizer) = Wx::BoxSizer->new( wxHORIZONTAL );
    $fluid_mat_prop_sizer->Add($fluid_cL_sizer,
                   1,
                   wxEXPAND|wxALL,
                   0);
    # Define Label of c_F:
    $fluid_cL_sizer->Add(Wx::StaticText->new($this->{toppanel},
                   -1,
                   "c_F",
                   wxDefaultPosition,
                   Wx::Size->new(-1,-1),
                   wxALIGN_RIGHT),
                   1,
                   wxEXPAND|wxALL,
                   5);
    # Define Control for c_F
    $this->{c_F_Control} = Wx::TextCtrl->new($this->{toppanel},
                   -1, # id
                   wxTE_RIGHT|wxTE_PROCESS_ENTER);
    $fluid_cL_sizer->Add($this->{c_F_Control},
                   1,
                   wxEXPAND|wxALL,
                   5);
    # Define unit label
    $fluid_cL_sizer->Add(Wx::StaticText->new($this->{toppanel},
                   -1,
                   "m/s"),
                   1,
                   wxEXPAND|wxALL,
                   5);
    #############
    # Fluid density
    my ($fluid_rho_sizer) = Wx::BoxSizer->new( wxHORIZONTAL );
    $fluid_mat_prop_sizer->Add($fluid_rho_sizer,
                   1,
                   wxEXPAND|wxALL,
                   0);
    # Define Label of rho_P:
    $fluid_rho_sizer->Add(Wx::StaticText->new($this->{toppanel},
                   -1,
                   "rho_F",
                   wxDefaultPosition,
                   Wx::Size->new(-1,-1),
                   wxALIGN_RIGHT),
                   1,
                   wxEXPAND|wxALL,
                   5);
    # Define Control for rho_F
    $this->{F_rho_Control} = Wx::TextCtrl->new($this->{toppanel},
                   -1, # id
                   wxTE_RIGHT|wxTE_PROCESS_ENTER);
    $fluid_rho_sizer->Add($this->{F_rho_Control},
                   1,
                   wxEXPAND|wxALL,
                   5);
    # Define unit label
    $fluid_rho_sizer->Add(Wx::StaticText->new($this->{toppanel},
                   -1,
                   "kg/m³"),
                   1,
                   wxEXPAND|wxALL,
                   5);
    #########
    # Create a "Load Material Properties"-Button for Fluids button
    my ($Load_Fluid_Material_Btn) =Wx::Button->new( $this->{toppanel}, $Load_fluid_props_Btn_ID,
                  "Load Material Properties" );
    # Add button to sizer
    $fluid_mat_prop_sizer->Add($Load_Fluid_Material_Btn,
                  0, # make horizontally unstretchable
                  wxEXPAND|wxALL, # make border all around (implicit top alignment)
                  5 ); # set border width to 5
    #############

    #------------------------
    #------------------------
    # Inversion start button and status display

    # Boxed Sizer for all controls
    my $box_calculation=Wx::StaticBox->new(
                   $this->{toppanel},
                   -1,
                   "Calculation",Wx::Point->new(-1,-1),Wx::Size->new(100,100));
    # Create vertical sizer, top part will contain inversion initial values,
    my ($calculation_sizer) = Wx::StaticBoxSizer->new($box_calculation,wxVERTICAL);
    # Add sizer to top sizer
    $this->{toppanel}->{topsizer}->Add($calculation_sizer,
                  0, # horizontally stretchable
                  wxEXPAND|wxALL,
                  10);
    # Inversion selection controls:

    # Create group of Radiobuttons
    $this->{inversion_selections} = ['Fit a log-normal size distribution to match attenuation dara','Phillips-Twomey algorithm (direct inversion)'];

    $this->{inversion_type_control} = Wx::RadioBox->new($this->{toppanel},
                  -1,
                  "Inversion type",
                  wxDefaultPosition,
                  wxDefaultSize,
                  $this->{inversion_selections},
                  2,                # # of cols or rows
                  wxRA_SPECIFY_COLS); # style

    # Add radio button group to top sizer
    $calculation_sizer->Add($this->{inversion_type_control},
                  0,
                  wxEXPAND|wxALL,
                  5);

    # initial values sizer
    my $box_initial_values=Wx::StaticBox->new(
                   $this->{toppanel},
                   -1,
                   "Initial Values",Wx::Point->new(-1,-1),Wx::Size->new(100,100));
    my ($initial_values_sizer) = Wx::StaticBoxSizer->new($box_initial_values,wxHORIZONTAL);
    $calculation_sizer->Add($initial_values_sizer,
                  0, # horizontally stretchable
                  wxEXPAND|wxALL,
                  5);
    #------------------------
    #------------------------
    ### Initial Values input
    # Checkbox for initial value input activation
    $this->{activate_initial_values} = Wx::CheckBox->new($this->{toppanel},
                  -1, # ID
                  "Use initial values", # Label
                  wxDefaultPosition, # Position
                  Wx::Size->new(-1,-1) # Size
    );
    $initial_values_sizer->Add($this->{activate_initial_values},
                  0, # horizontally stretchable
                  wxEXPAND|wxALL,
                  5);
    #############
    # x50
    my ($x50_sizer) = Wx::BoxSizer->new( wxHORIZONTAL );
    $initial_values_sizer->Add($x50_sizer,
                   1,
                   wxEXPAND|wxALL,
                   0);
    # Define Label of x50:
    $x50_sizer->Add(Wx::StaticText->new($this->{toppanel},
                   -1,
                   "x_50",
                   wxDefaultPosition,
                   Wx::Size->new(-1,-1),
                   wxALIGN_RIGHT),
                   1,
                   wxEXPAND|wxALL,
                   5);
    # Define Control for x_50
    $this->{x50_Control} = Wx::TextCtrl->new($this->{toppanel},
                   -1, # id
                   wxTE_RIGHT|wxTE_PROCESS_ENTER);
    $x50_sizer->Add($this->{x50_Control},
                   1,
                   wxEXPAND|wxALL,
                   5);
    # Define unit label
    $x50_sizer->Add(Wx::StaticText->new($this->{toppanel},
                   -1,
                   "mum",
                   wxDefaultPosition,
                   Wx::Size->new(-1,-1),
                   wxALIGN_LEFT),
                   1,
                   wxEXPAND|wxALL,
                   5);

    #########

    #############
    # sigma_ln
    my ($sigma_ln_sizer) = Wx::BoxSizer->new( wxHORIZONTAL );
    $initial_values_sizer->Add($sigma_ln_sizer,
                   1,
                   wxEXPAND|wxALL,
                   0);
    # Define Label of sigma_ln:
    $sigma_ln_sizer->Add(Wx::StaticText->new($this->{toppanel},
                   -1,
                   "sigma_ln",
                   wxDefaultPosition,
                   Wx::Size->new(-1,-1),
                   wxALIGN_RIGHT),
                   1,
                   wxEXPAND|wxALL,
                   5);
    # Define Control for sigma_ln
    $this->{sigma_ln_Control} = Wx::TextCtrl->new($this->{toppanel},
                   -1, # id
                   wxTE_RIGHT|wxTE_PROCESS_ENTER);
    $sigma_ln_sizer->Add($this->{sigma_ln_Control},
                   1,
                   wxEXPAND|wxALL,
                   5);
    # Define unit label
    $sigma_ln_sizer->Add(Wx::StaticText->new($this->{toppanel},
                   -1,
                   ""),
                   1,
                   wxEXPAND|wxALL,
                   5);
    #########
    #############
    # phi
    my ($phi_sizer) = Wx::BoxSizer->new( wxHORIZONTAL );
    $initial_values_sizer->Add($phi_sizer,
                   1,
                   wxEXPAND|wxALL,
                   0);
    # Define Label of phi:
    $phi_sizer->Add(Wx::StaticText->new($this->{toppanel},
                   -1,
                   "phi",
                   wxDefaultPosition,
                   Wx::Size->new(-1,-1),
                   wxALIGN_RIGHT),
                   1,
                   wxEXPAND|wxALL,
                   5);
    # Define Control for phi
    $this->{phi_Control} = Wx::TextCtrl->new($this->{toppanel},
                   -1, # id
                   wxTE_RIGHT|wxTE_PROCESS_ENTER);
    $phi_sizer->Add($this->{phi_Control},
                   1,
                   wxEXPAND|wxALL,
                   5);
    # Define unit label
    $phi_sizer->Add(Wx::StaticText->new($this->{toppanel},
                   -1,
                   "% by vol."),
                   1,
                   wxEXPAND|wxALL,
                   5);
    #########

    # calculation sizer
    my ($calculation_sub_sizer) = Wx::BoxSizer->new(wxHORIZONTAL);
    $calculation_sizer->Add($calculation_sub_sizer,
                  0, # horizontally stretchable
                  wxEXPAND|wxALL,
                  0);

    # Create Start Button
    $this->{start_calculation_Btn} =Wx::Button->new( $this->{toppanel},
                  wxID_OK, "Start Calculation" );
    # Add button to sizer
    $calculation_sub_sizer->Add($this->{start_calculation_Btn},
                  0, # make horizontally unstretchable
                  wxEXPAND|wxALL, # make border all around (implicit top alignment)
                  10 ); # set border width to 10

    # Create a sizer that will hold a little status window and buttons for displaying results
    my ($status_sizer) = Wx::StaticBoxSizer->new(Wx::StaticBox->new($this->{toppanel},
                  -1,
                  "Status / Result",wxDefaultPosition,wxDefaultSize),
                  wxVERTICAL);
    # Add to calculation sizer
    $calculation_sub_sizer->Add($status_sizer,
                  1, # make horizontally unstretchable
                  wxEXPAND|wxALL, # make border all around (implicit top alignment)
                  5 ); # set border width to 5

    $this->{status_display} = Wx::TextCtrl->new($this->{toppanel},
                   -1, # id
                  "",  #  Default value
                  wxDefaultPosition,
                  Wx::Size->new(400,100),
                  wxTE_PROCESS_ENTER|wxTE_MULTILINE|wxTE_READONLY );
    # Add control to status_sizer
    $status_sizer->Add($this->{status_display},
                  0, # make horizontally unstretchable
                  wxEXPAND|wxALL, # make border all around (implicit top alignment)
                  5 ); # set border width to 5

    # Add a sizer that will hold the buttons.
    my ($result_buttons_sizer) = Wx::BoxSizer->new( wxHORIZONTAL );
    $status_sizer->Add($result_buttons_sizer,
                  1,
                  wxEXPAND|wxALL,
                  0);
    # Add a button for display of particle size distribution graph
    $this->{display_PGV_Graph_Btn}=Wx::Button->new( $this->{toppanel}, $Display_graph_Btn_ID,
                 "Display particle size distribution graph" );
    # Add button to sizer
    $result_buttons_sizer->Add($this->{display_PGV_Graph_Btn},
                  0, # make horizontally unstretchable
                  wxEXPAND|wxALL, # make border all around (implicit top alignment)
                  10 ); # set border width to 10
    # disable the button, beause we first need to calculate the results.
    $this->{display_PGV_Graph_Btn}->Enable(0);
    # Add a button for display of particle size distribution table
    $this->{display_PGV_Table_Btn} =Wx::Button->new( $this->{toppanel}, $Display_table_Btn_ID,
                 "Display particle size distribution values" );
                 #
    # Add button to sizer
    $result_buttons_sizer->Add($this->{display_PGV_Table_Btn},
                  0, # make horizontally unstretchable
                  wxEXPAND|wxALL, # make border all around (implicit top alignment)
                  10 ); # set border width to 10
    # disable the button, beause we first need to calculate the results.
    $this->{display_PGV_Table_Btn}->Enable(0);
    # Add a button for display of attenuation spectrum graph
    $this->{display_attn_Graph_Btn} =Wx::Button->new( $this->{toppanel}, $Display_attn_Graph_Btn_ID,
                 "Attenuation Graph" );
                 #
    # Add button to sizer
    $result_buttons_sizer->Add($this->{display_attn_Graph_Btn},
                  0, # make horizontally unstretchable
                  wxEXPAND|wxALL, # make border all around (implicit top alignment)
                  10 ); # set border width to 10
    # disable the button, beause we first need to calculate the results.
    $this->{display_attn_Graph_Btn}->Enable(0);

    #------------------------
    # Assign topsizer
    $this->SetSizer( $this->{toppanel}->{topsizer} );
    $this->SetAutoLayout(1);
    $this->{toppanel}->{topsizer}->Fit($this->{toppanel});
    # Assign default control
    $this->{toppanel}->SetDefaultItem($this->{start_calculation_Btn});
    $this->Fit();

    #------------------------
    # Create process handle (for communication with fit sub program )
    #------------------------
    $this->{fit_process}= Wx::Process->new($this , $fit_process_ID);
    # turn on output redirection so we can read the results
    $this->{fit_process}->Redirect;

    #------------------------
    # Assign events
    #------------------------

    # Attenuation Data Load Button event
    EVT_BUTTON($this,          # Object to bind to
              $load_attn_file_Btn,         # ButtonID
              \&OnLoad_attenuation_Btn_Clicked # Subroutine to execute
             );

    # Particle Properties Load button event
    EVT_BUTTON($this,          # Object to bind to
              $Load_Particle_Material_Btn,         # ButtonID
              \&OnLoad_particle_material_Btn_Clicked # Subroutine to execute
             );

    EVT_BUTTON($this,          # Object to bind to
              $Load_Fluid_Material_Btn,         # ButtonID
              \&OnLoad_fluid_material_Btn_Clicked # Subroutine to execute             );
             );
    EVT_BUTTON($this,          # Object to bind to
              $this->{start_calculation_Btn},         # ButtonID
              \&start_calculation_Btn_Clicked # Subroutine to execute             );
             );
    EVT_BUTTON($this,          # Object to bind to
              $this->{display_PGV_Table_Btn},         # ButtonID
              \&display_result_table_Btn_Clicked # Subroutine to execute             );
             );
    EVT_BUTTON($this,          # Object to bind to
              $this->{display_PGV_Graph_Btn},         # ButtonID
              \&display_result_graph_Btn_Clicked # Subroutine to execute             );
             );
    EVT_BUTTON($this,          # Object to bind to
              $this->{display_attn_Graph_Btn},         # ButtonID
              \&display_attn_Graph_Btn_Clicked # Subroutine to execute             );
             );
    # Assign events
    EVT_END_PROCESS($this,
          $fit_process_ID,
          \&On_End_fit_process
          );

    #------------------------
    # Fill with initial data
    #------------------------
    #
    # Particle properties. File name is set at beginning of file
    my $message="";
    my $particle_data_read = $this->{inversion}->{particle_properties}->read_property_file($particle_file);
    if ($particle_data_read>0) {
      # fill fields with values
      $this->fill_particle_properties_fields;
      unless (($particle_data_read && 1)==1) {
        $message = $message . "C_P,L\n";
      }
      unless (($particle_data_read && 2)==2) {
        $message = $message . "C_P,T\n";
      }
      unless (($particle_data_read && 4)==4) {
        $message = $message . "rho_P\n";
      }
      unless (($particle_data_read && 7)==7) {
        Wx::MessageBox( "The following material properties could not be read from $particle_file\n" .
        $message , 'Warning', wxOK, $this )
      }
    }
    else{
      Wx::MessageBox( "Couldn't open $particle_file, or data not found inside file" , 'Error', wxOK, $this )
    }
     #
     # Fluid properties.
     #
    if ($this->{inversion}->{fluid_properties}->read_property_file($fluid_file)) {
      # fill fields with values
      $this->fill_fluid_properties_fields;
    }
    else{
      Wx::MessageBox( "Couldn't open $fluid_file" , 'Error', wxOK, $this )
    }
     #
     # Attenuation
     #
    if ($this->{inversion}->{attn}->read_Kext($attenuation_file)) {
      # fill grid with values
      $this->fill_attenuation_data_field;
    }
    else{
      Wx::MessageBox( "Couldn't open $attenuation_file" , 'Error', wxOK, $this )
    }

     #
     # Initial Values for Inversion
     #
    $this->{x50_Control}->SetValue(40);
    $this->{sigma_ln_Control}->SetValue(0.1);
    $this->{phi_Control}->SetValue(1.0);
    return $this;


}

# these are some constants used by Wx::MessageBox
use Wx qw(wxOK wxICON_INFORMATION wxVERSION_STRING);

# called when the user selects the 'Exit' menu item
sub OnQuit {
    my( $this, $event ) = @_;

    #Wx::MessageBox( 'OnQuit: Exiting', 'Message', wxOK, $this );

    # closes the frame
    $this->Close( 1 );
}

# called when the user selects the 'About' menu item
sub OnAbout {
    my( $this, $event ) = @_;

    # display a simple about box
    Wx::MessageBox( "MVT-TUD US Analyzer \n\n" .
                    "Simple frontend for performing analysis\n" .
                    "of measured sound attenuation spectra\n" .
                    "to give resulting particle size distributions.\n\n" .
                    "(C) 2003-2004 Andreas Richter,\n" .
                    "TU Dresden, Institut für Verfahrenstechnik und Umwelttechnik\n\n" .
                    "Program versions:\n" .
                    "wxPerl " . $Wx::VERSION . "\n" .
                    wxVERSION_STRING,
                    "About", wxOK | wxICON_INFORMATION,
                    $this );
}

sub OnSelectDatabase {
    my( $this, $event ) = @_;
    my $database_current =$this->{rec_set}->database;
    #printf $database_current . "\n";
    #printf $this->{rec_set}->default_dir . "\n";
    # Create Dialog
    my ($database_file_dialog) = Wx::FileDialog->new($this,
                    "Select DT Database File (data_aco.mdb)",
                    $this->{rec_set}->default_dir, # default directory
                    $database_current, # default file
                    "data_aco.mdb", # wildcard
                    wxOPEN, # style
                    wxDefaultPosition);
    # Show Dialog, and check the return value
    if ($database_file_dialog->ShowModal==wxID_OK) {
      # Get File name
      my $database_test =$database_file_dialog->GetPath;
      $database_file_dialog->Destroy;
      # Verify database by opening it
      $this->{rec_set}->database($database_test);
      #printf dirname($database_test) . "\n";
      unless ($this->{rec_set}->load_recordsetdescriptions ) {
        # Database is not available or not of the format we want
        $this->{rec_set}->database($database_current);
        Wx::MessageBox( "Database does not appear to be of the correct format.\n" .
           "Please select another file."  , 'Error', wxOK, $this );
        #$this->OnSelectDatabase;
      }
      $this->{rec_set}->default_dir(dirname($database_test));
    }
  $database_file_dialog->Destroy;
}

sub OnClose {
    my( $this, $event ) = @_;
    #if ($this->{attn_graph}) { $this->{attn_graph}->Destroy }
    #Wx::MessageBox( 'OnClose: Closing', 'OnClose', wxOK, $this );
    # Destroy dialog handle
    if ($^O =~'Win32') {
      $this->{rec_set}->Destroy();
    }

    $event->Skip();
}

sub OnLoad_attenuation_Btn_Clicked {
    my( $this, $event ) = @_;
    # Check the OS
    if ($^O =~'Win32') {
      # We are running under Windows, which means we can use the
      # DT database to obtain our data
      if ($this->{rec_set}->ShowModal()) {
        # we clicked OK
        # get data from Dialog (which does the database extraction stuff)
        $this->{inversion}->{attn}->alpha_0($this->{rec_set}->get_blank_data_from_DB);
        $this->{inversion}->{attn}->alpha($this->{rec_set}->get_measurement_data_from_DB);
        #$this->{inversion}->{attn}->f(pdl [3, 3.69, 4.53, 5.57, 6.84, 8.4, 10.32, 12.69, 15.59, 19.15, 23.53, 28.91, 35.53, 43.65, 53.64, 65.9, 80.9, 99]);
        $this->{inversion}->{attn}->f($this->{rec_set}->{attn_data}->f);
        # Get material properties
        $this->{inversion}->{fluid_properties}->c_L($this->{rec_set}->{fluid}->c_L);
        $this->{inversion}->{fluid_properties}->rho($this->{rec_set}->{fluid}->rho);
        $this->{inversion}->{particle_properties}->c_L($this->{rec_set}->{particle}->c_L);
        $this->{inversion}->{particle_properties}->rho($this->{rec_set}->{particle}->rho);        

        # Fill data into our attenuation data control
        $this->fill_attenuation_data_field;
        $this->fill_particle_properties_fields;
        $this->fill_fluid_properties_fields;
        Wx::MessageBox("Please note that the value of c_P,T cannot be taken from \n" .
         "the database, because it is not stored there.\n".
         "Please enter a value in the corresponding field.",
          'Information', wxOK, $this );
      }
    }
    else {
      # We are not running under Windows, which means we have to get the data
      # from a different source (text file)
      #
      # Create Dialog that allows selection of a text file in which attenuation
      # data is stored
      my ($attenuation_file_dialog) = Wx::FileDialog->new($this,
                    "Select attenuation data file",
                    File::Spec->catfile('Examples'), # default directory
                    "", # default file
                    "*.txt", # wildcard
                    wxOPEN, # style
                    wxDefaultPosition);
      # Show Dialog, and check the return value
      if ($attenuation_file_dialog->ShowModal==wxID_OK) {
        # Get File name
        my $attenuation_file = $attenuation_file_dialog->GetPath;
        # Extract data from file (call subrouine)
        if ($this->{inversion}->{attn}->read_Kext($attenuation_file)) {
          # fill grid with values
          $this->fill_attenuation_data_field;
        }
        else{
          Wx::MessageBox( "Couldn't open $attenuation_file" , 'Error', wxOK, $this )
        }
      }
      # Destroy Dialog
      $attenuation_file_dialog->Destroy;
    }
}

sub fill_attenuation_data_field {
  my( $this) = @_;
  # Adjust number of columns
  my $dims =$this->{inversion}->{attn}->alpha->getdim("0");
  if ( $dims > $this->{grid}->GetNumberCols) {
     $this->{grid}->AppendCols($dims - $this->{grid}->GetNumberCols);
  } else {
    if ($dims<$this->{grid}->GetNumberCols) {
      $this->{grid}->DeleteCols(0,$this->{grid}->GetNumberCols-$dims);
    }
  }
  for(my $i=0;$i<$dims;$i++){
    $this->{grid}->SetCellValue(0,$i, $this->{inversion}->{attn}->alpha_elem($i));
    $this->{grid}->SetColFormatFloat($i,4,2);
    $this->{grid}->SetColLabelValue($i, $this->{inversion}->{attn}->f_elem($i));
  }
  # Adjust column width
  $this->{grid}->AutoSizeColumns(1);
}

sub get_attenuation_data_from_fields {
  my( $this) = @_;
  for(my $i=0;$i<$this->{inversion}->{attn}->alpha->getdim("0");$i++){
    #$this->{grid}->SetCellValue(0,$i, $this->{inversion}->alpha_elem($i));
    $this->{inversion}->{attn}->alpha_elem($i, $this->{grid}->GetCellValue(0,$i));
  }
}


sub OnLoad_particle_material_Btn_Clicked {
    my( $this, $event ) = @_;

    # Create Dialog
    my ($particle_file_dialog) = Wx::FileDialog->new($this,
                     "Select particle properties file",
                     File::Spec->catfile('Examples'), # default directory
                     "", # default file
                     "*.txt", # wildcard
                     wxOPEN, # style
                     wxDefaultPosition);
    # Show Dialog, and check the return value
    if ($particle_file_dialog->ShowModal==wxID_OK) {
       # Get File name
       my $particle_file = $particle_file_dialog->GetPath;
       my $message = "";
       # Extract data from file (call subrouine)
       my $particle_data_read = $this->{inversion}->{particle_properties}->read_property_file($particle_file);
       if ($particle_data_read>0) {
         # Test if values are missing
         $message .= "c_P,L\n" unless (($particle_data_read & 1)==1);
         $message .= "c_P,T\n" unless (($particle_data_read & 2)==2);
         $message .= "rho_P\n" unless (($particle_data_read & 4)==4);
         unless (($particle_data_read & 7)==7) {
            Wx::MessageBox( "The following material properties could not be read from $particle_file:\n" .
            $message , 'Warning', wxOK, $this )
         }
         # fill fields with values
         $this->fill_particle_properties_fields;
       }
       else{
         Wx::MessageBox( "Couldn't open $particle_file" , 'Error', wxOK, $this )
       }
    }
    # Destroy Dialog
    $particle_file_dialog->Destroy;
}

sub fill_particle_properties_fields {
  my( $this) = @_;
  $this->{c_P_L_Control}->SetValue($this->{inversion}->{particle_properties}->c_L);
  $this->{c_P_T_Control}->SetValue($this->{inversion}->{particle_properties}->c_T);
  $this->{P_rho_Control}->SetValue($this->{inversion}->{particle_properties}->rho);
}

sub get_particle_properties_from_fields {
  my( $this) = @_;
  #$this->{c_P_L_Control}->SetValue($this->{inversion}->{particle_properties}->c_L);
  $this->{inversion}->{particle_properties}->c_L($this->{c_P_L_Control}->GetValue);
  #$this->{c_P_T_Control}->SetValue($this->{inversion}->{particle_properties}->c_T);
  $this->{inversion}->{particle_properties}->c_T($this->{c_P_T_Control}->GetValue);
  #$this->{P_rho_Control}->SetValue($this->{inversion}->{particle_properties}->rho);
  $this->{inversion}->{particle_properties}->rho($this->{P_rho_Control}->GetValue);
}


sub OnLoad_fluid_material_Btn_Clicked {
    my( $this, $event ) = @_;

    # Create Dialog
    my ($fluid_file_dialog) = Wx::FileDialog->new($this,
                     "Select fluid properties file",
                     File::Spec->catfile('Examples'), # default directory
                     "", # default file
                     "*.txt", # wildcard
                     wxOPEN, # style
                     wxDefaultPosition);
    # Show Dialog, and check the return value
    if ($fluid_file_dialog->ShowModal==wxID_OK) {
       # Get File name
       my $fluid_file = $fluid_file_dialog->GetPath;
       # Extract data from file (call subrouine)
       my $message = "";
       my $fluid_data_read =
         $this->{inversion}->{fluid_properties}->read_property_file($fluid_file);
       if ($fluid_data_read>0) {
         # Test if values are missing
         $message .= "c_F\n" unless (($fluid_data_read & 1)==1);
         $message .= "rho_F\n" unless (($fluid_data_read & 4)==4);
         unless (($fluid_data_read & 5)==5) {
            Wx::MessageBox( "The following material properties could not be read from $fluid_file:\n" .
            $message , 'Warning', wxOK, $this )
         }
         # fill fields with values
         $this->fill_fluid_properties_fields;
       }
       else{
         Wx::MessageBox( "Couldn't open $fluid_file" , 'Error', wxOK, $this )
       }
    }
    # Destroy Dialog
    $fluid_file_dialog->Destroy;
}

sub fill_fluid_properties_fields {
  my( $this) = @_;
  $this->{c_F_Control}->SetValue($this->{inversion}->{fluid_properties}->c_L);
  $this->{F_rho_Control}->SetValue($this->{inversion}->{fluid_properties}->rho);
}

sub get_fluid_properties_from_fields {
  my( $this) = @_;
  #$this->{c_F_Control}->SetValue($this->{inversion}->{fluid_properties}->c_L);
  $this->{inversion}->{fluid_properties}->c_L($this->{c_F_Control}->GetValue);
  #$this->{F_rho_Control}->SetValue($this->{inversion}->{fluid_properties}->rho);
  $this->{inversion}->{fluid_properties}->rho($this->{F_rho_Control}->GetValue);
}

sub get_LNVT_initial_data_from_fields {
  my( $this) = @_;
  if ($this->{activate_initial_values}->IsChecked) {
    $this->{selected_inversion}->x_50_ini($this->{x50_Control}->GetValue);
    $this->{selected_inversion}->sigma_ln_ini($this->{sigma_ln_Control}->GetValue);
    $this->{selected_inversion}->phi_ini($this->{phi_Control}->GetValue);
  }
}

sub get_Phillips_Twomey_initial_data_from_fields {
  my ($this) = @_;
  if ($this->{activate_initial_values}->IsChecked) {
    $this->{selected_inversion}->x_50_ini($this->{x50_Control}->GetValue);
    $this->{selected_inversion}->phi_ini($this->{phi_Control}->GetValue);
  }
}

sub start_calculation_Btn_Clicked {
    my( $this, $event ) = @_;
    $this->get_attenuation_data_from_fields;
    $this->get_particle_properties_from_fields;
    $this->get_fluid_properties_from_fields;

    # Carry out the inversion based upon a logarithmic normal distributions
    # this should return an error value signaling successful operation (needed
    # for further operation steps)
    # 1. option: fit directly in program
    #$this->{inversion}->fit_lnvt;
    # 2. option: fit separately, in own module
    # Determine fit type
    if ($this->{inversion_type_control}->GetSelection==0) {
      $this->{selected_inversion}=inversionsrechnung_fit_LNVT->new($this);
      $this->get_LNVT_initial_data_from_fields;
    }
    else {
      $this->{selected_inversion}=inversionsrechnung_Phillips_Twomey->new($this);
      $this->get_Phillips_Twomey_initial_data_from_fields;
    }
    # Copy data to new inversion object
    $this->{selected_inversion}->{attn}=$this->{inversion}->{attn};
    $this->{selected_inversion}->{particle_properties}=$this->{inversion}->{particle_properties};
    $this->{selected_inversion}->{fluid_properties}=$this->{inversion}->{fluid_properties};
    # Start fit process
    ($this->{fit_process_id})=$this->{selected_inversion}->start_calculation($this->{fit_process});
    # 3. option: fit separately, in this module
    # Write material properties to files
#     $this->{inversion}->{fluid_properties}->write_property_file("fluid.txt");
#     $this->{inversion}->{particle_properties}->write_property_file("particle.txt");
#     # Write attenuation data to file
#     $this->{inversion}->{attn}->write_alpha("alpha_meas.txt");
#     # initiate calculation by calling the relevant script
#     $this->{fit_process_id} =Wx::ExecuteCommand("perl ". $inversion_script . " -k alpha_meas.txt -P particle.txt -F fluid.txt -r results.txt",
#                        0,  # asynchronous execution
#                        $this->{fit_process});
    # Check if the process started successfully
    if ( !$this->{fit_process_id} ) {
        #wxLogError( _T("Execution of '%s' failed."), cmd.c_str() );
        $this->{status_display}->AppendText("Start of curve fit failed.\n");
    }
    else {
        #wxLogStatus( _T("Process %ld (%s) launched."),
        $this->{status_display}->AppendText("Start of curve fit successful.\n");
        $this->{status_display}->AppendText("Performing curve fit...  ");
        # Change the button behavior and look
        # so that it can be used to interrupt the calculation
        $this->{start_calculation_Btn}->SetLabel("STOP");
        EVT_BUTTON($this,          # Object to bind to
              $this->{start_calculation_Btn},         # ButtonID
              \&stop_calculation_Btn_Clicked # Subroutine to execute             );
             );
       # Set up the timer to call the sub OnTimer which checks the output of the program
       my $timerid = Wx::NewId();
       $this->{timer} = Wx::Timer->new($this, $timerid);
       $this->{timer}->Start(10, 0); #the 0 means no one-shot
       EVT_TIMER($this, $timerid, \&OnTimer);
    }
}

sub OnTimer {
   my ($this,$event) = @_;
   while ($this->{fit_process}->IsInputAvailable) {
     my $linebuf=readline($this->{fit_process}->GetInputStream);
     $this->{status_display}->AppendText( $linebuf);
     chomp($linebuf);
     if ($this->{selected_inversion} =~ m/inversionsrechnung_fit_LNVT/i) {
       #wxTextInputStream tis(*GetInputStream());
       if ( ($linebuf =~ "x50 ") && ($linebuf =~ ", sigma_ln ") && ($linebuf =~ ", phi ")) {
         my @fields = split(", ", $linebuf);
         my @x50_fields = split(" ",$fields[0]);
         my @sigmaln_fields = split(" ",$fields[1]);
         my @phi_fields= split(" ",$fields[2]);
         $this->{selected_inversion}->x50($x50_fields[1]);
         $this->{selected_inversion}->sigma_ln($sigmaln_fields[1]);
         $this->{selected_inversion}->c($phi_fields[1]);
       }
     }
     # inversion by direct matrix calculation
     else {
       $this->{selected_inversion}->read_results($linebuf);
     }
   }
}

sub stop_calculation_Btn_Clicked {
   my ($this,$event) = @_;
   if (Wx::Process::Exists($this->{fit_process_id})) {
     Wx::Process::Kill($this->{fit_process_id},wxSIGKILL);
   }
   #$this->{fit_process}->
   $this->{start_calculation_Btn}->SetLabel("Start Calculation");
        EVT_BUTTON($this,          # Object to bind to
              $this->{start_calculation_Btn},         # ButtonID
              \&start_calculation_Btn_Clicked # Subroutine to execute             );
             );
}

sub On_End_fit_process {
  my ($this,$event) = @_;
#  my $stream_out=$this->{fit_process}->GetInputStream;
#  printf $stream_out->ReadLine() . "\n";
#  $this->{status_display}->AppendText("Done\n");
  # Read inversion results
  # If successful, display them along with some status info
  # enable the "Display Table", "Display Graph" buttons
  $this->{display_PGV_Table_Btn}->Enable(1);
  $this->{display_PGV_Graph_Btn}->Enable(1);
  $this->{display_attn_Graph_Btn}->Enable(1);
  # fit_LNVT-Inversion
  if ($this->{selected_inversion} =~ m/inversionsrechnung_fit_LNVT/i) {
    $this->{status_display}->AppendText("Result of least error squares fit:\n");
    $this->{status_display}->AppendText("x50: " . $this->{selected_inversion}->x50*1e6 . "µm \n");
    $this->{status_display}->AppendText("sigma_ln: " . $this->{selected_inversion}->sigma_ln . "\n");
    $this->{status_display}->AppendText("phi: " . $this->{selected_inversion}->c*100 . " Vol-%\n");
    # Calculate resulting size distribution
    $this->{selected_inversion}->calculate_x;
    $this->{selected_inversion}->dQ_from_params;
  } else {
    #$this->{selected_inversion}->read_results;
  }
  $this->{inversion}->xm($this->{selected_inversion}->xm);
  $this->{inversion}->dQ($this->{selected_inversion}->dQ);
  $this->{inversion}->c($this->{selected_inversion}->c);
  $this->{inversion}->calculate_q;
  #$this->{fit_process}->Destroy;
  # Set Label of button back to Start
  $this->{start_calculation_Btn}->SetLabel("Start Calculation");
  # Reassign event
      EVT_BUTTON($this,          # Object to bind to
            $this->{start_calculation_Btn},         # ButtonID
            \&start_calculation_Btn_Clicked # Subroutine to execute             );
           );
}

sub display_result_table_Btn_Clicked {
    my( $this, $event ) = @_;
    # Create a table frame
    $this->{frame} = ResultWindow->new( "Result",
               wxDefaultPosition,
               wxDefaultSize,
               wxDEFAULT_FRAME_STYLE|wxFRAME_FLOAT_ON_PARENT,
               $this->{inversion}->xm->getdim(0));

    # Fill in values
    $this->{frame}->xm($this->{inversion}->xm);
    $this->{frame}->fill_x_values;
    $this->{frame}->dQ($this->{inversion}->dQ);
    $this->{frame}->fill_Q_values;
    #$this->{display_PGV_Table_Btn}->Enable(0);

    $this->{frame}->Show( 1 );
}

sub display_result_graph_Btn_Clicked {
    my( $this, $event ) = @_;
    # Create the window which will show the result
    $this->{graph} = ResultGraph->new( "Result",
               wxDefaultPosition,
#              [450, 350]
               [300, 500],
               wxDEFAULT_FRAME_STYLE|wxFRAME_FLOAT_ON_PARENT,
               $this->{inversion}->xm->getdim(0));
    # the following is not nice and should be done in a way
    # adapted to OOP
    # copy data to graph
    $this->{graph}->xm($this->{inversion}->xm);
    $this->{graph}->dQ($this->{inversion}->dQ);
    $this->{graph}->q($this->{inversion}->q);

    $this->{graph}->Create_Graph;
}

sub display_attn_Graph_Btn_Clicked {
    my( $this, $event ) = @_;
    # Create the window which will show the result
    $this->{attn_graph} = AttenuationGraph->new( "Attenuation",
               wxDefaultPosition,
#              [450, 350]
               [300, 500],
               wxDEFAULT_FRAME_STYLE|wxFRAME_FLOAT_ON_PARENT,
               $this->{inversion}->{attn}->f->getdim("0"));
    # the following is not nice and should be done in a way
    # adapted to OOP
    # copy data to graph
    $this->{attn_graph}->f($this->{inversion}->{attn}->f);
    $this->{attn_graph}->alpha($this->{inversion}->{attn}->alpha);
    $this->{attn_graph}->alpha_0($this->{inversion}->{attn}->alpha_0);
    $this->{inversion}->calculate_parameters;
    $this->{attn_graph}->alpha_calc(alpha_Str_Faran($this->{inversion}->{attn}->f,
                          $this->{inversion}->xm,
                          $this->{inversion}->dQ,
                          $this->{inversion}->c_l,
                          $this->{inversion}->h_l,
                          $this->{inversion}->h_t,
                          $this->{inversion}->g,
                          $this->{inversion}->c)*$dB
                          +$this->{inversion}->{attn}->alpha_0*(1-$this->{inversion}->c));
    #$this->{graph}->dQ_from_params;

    $this->{attn_graph}->Create_Graph;
}



1;
