#!/usr/bin/env perl
use strict;
use warnings;
use CGI ":standard";
use CGI::Carp qw(warningsToBrowser fatalsToBrowser);
use Data::Dumper;
use LWP::UserAgent ();
use XML::LibXML;
use Encode;
use URI;
use Benchmark;
use JSON;
use utf8;
use feature qw/say/;
use Encode::Deep;
use DBD::mysql;
use URI::Encode qw(uri_encode uri_decode);
use URI::Split qw(uri_split uri_join);
use Data::Dumper;
use HTML::TreeBuilder::XPath;

# LWP
my $ua = LWP::UserAgent->new(keep_alive => 1);
$ua->agent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36');
$ua->timeout(10);
$ua->env_proxy;

# CGI
my $cgi = CGI->new;

# GLOBAL
my %CONFIG =
        (
            'DEBUG' => 0,
            'DEBUG_PRINT_DATA' => 0,
            'ONLINE' => 1,
            'USE_CORSANYWHERE' => 0,
            'OFFLINE_XML' => '../seatmap-client/offline/MapTicketSales.xml',
            'OFFLINE_XML_PARKETT' => '../seatmap-client/offline/MapTicketSales_Parkett.xml',
            'OFFLINE_XML_RANG' => '../seatmap-client/offline/MapTicketSales_Rang.xml',
            'MYSQL' => {
                        'database'  => 'seatmap',
                        'hostname'  => 'localhost',
                        'user' => 'seatmap_p',
                        'pw'  => 'r4Iybg75&3Q@9N!*',
                        'port'  => 3306,
            },
        );
my %DATA;
my $connection;

# WORKFLOW
my $t0 = Benchmark->new if $CONFIG{'DEBUG'};

&getParameter();
print Dumper $DATA{'PARAMETER'} if $CONFIG{'DEBUG'};
&checkHost();

if( $DATA{'PARAMETER'}{'request_switch'} eq 'seatmapWorkflow' ) {
    say 'Switch seatmapWorkflow' if ($CONFIG{'DEBUG'});
    &generateVenueUrl();

    my $xml;
    if ($CONFIG{'ONLINE'}) {
        print "SET AS ONLINE\n" if $CONFIG{'DEBUG'};
        
        $xml = &get($DATA{'PARAMETER'}{'urlVenue'});
        &loadXML($xml, \$DATA{'DOM'});
        &parseXML();
        &getSeatmaps();
    }
    elsif (!$CONFIG{'ONLINE'}) {
        print "SET AS OFFLINE: USING LOCAL XML\n" if $CONFIG{'DEBUG'};
        $xml = $CONFIG{'OFFLINE_XML'};
        
        &loadXML($xml, \$DATA{'DOM'});
        &parseXML();
        &loadXML($CONFIG{'OFFLINE_XML_PARKETT'}, \$DATA{'DOM'});
        parxeXML_section();
        &loadXML($CONFIG{'OFFLINE_XML_RANG'}, \$DATA{'DOM'});
        parxeXML_section();
    }
    my $config_ref = &getVenueConfig();
    &printOutput($config_ref);
}
elsif( $DATA{'PARAMETER'}{'request_switch'} eq 'checkoutCheckResponse' ) {
    say 'Switch checkoutCheckResponse' if ($CONFIG{'DEBUG'});
    my $decoded_content = &get($DATA{'PARAMETER'}{'url'});
    #print Dumper $decoded_content;
    
    my $tree = HTML::TreeBuilder::XPath->new_from_content($decoded_content);
    
    my $xpath_alert = '//input[@id="no_timeout_error_msg"]/@value';
    if ($tree->exists($xpath_alert)) {
        #my $alert = decode('utf-8', trim($tree->findvalue($xpath_alert)));
        
        my %alert;
        $alert{'no_timeout_error_msg'}{'value'} = trim($tree->findvalue($xpath_alert));
        $alert{'no_timeout_error_msg'}{'id'} = 'no_timeout_error_msg';
        
        
        if (!$CONFIG{'DEBUG'}) {
            print header(
              -type => 'application/json',
              -access_control_allow_origin => '*',
              -charset => 'utf-8',
            );
            
            # OUTPUT JSON
            print encode_json \%alert;
        }
        elsif ($CONFIG{'DEBUG'}) {
            print Dumper \%alert;
        }
    
    
    }
}

&benchmarkEnd() if ($CONFIG{'DEBUG'});


# SUBS

sub trim {
    my $string = shift;
    $string =~ s/^\s+|\s+$//g;
    return $string;
}

sub db_getVenueConfig {
    &Delimiter((caller(0))[3]);

    my $query = "Select \
                    venues.AGENCY As VENUE_AGENCY,
                    venues.NAME As VENUE_NAME,
                    halls.NAME As HALL_NAME,
                    halls.CODE As HALL_CODE,
                    halls.`FRONT-INDICATOR_WIDTH` As HALL_FRONT_INDICATOR,
                    halls.`PADDING-LEFT` As HALL_PADDING_LEFT,
                    halls.`PADDING-LEFT-STAGE` As HALL_PADDING_LEFT_STAGE,
                    halls.MAX_TICKETS_PER_USER As HALL_MAX_TICKETS_PER_USER,
                    halls.BUEHNE As HALL_BUEHNE,
                    halls.LEFT_NAMING As HALL_LEFT_NAMING,
                    halls.TONPULT As HALL_TONPULT,
                    halls.CSS As HALL_CSS,
                    halls.DESCRIPTION As HALL_DESC,
                    buyer_types.CODE As BUYER_TYPE_CODE,
                    buyer_types.DESCRIPTION As BUYER_TYPE_DESC,
                    halls.ID As HALL_ID
                From
                    venues Inner Join
                    halls On halls.VENUE_ID = venues.ID Inner Join
                    buyer_types On buyer_types.VENUE_ID = halls.VENUE_ID
                Where
                    venues.AGENCY =  '$DATA{'PARAMETER'}{'supplier_code'}'";
                    
    print Dumper $query if $CONFIG{'DEBUG'};
    my $response = &db_query($query, $connection, 'selectall_arrayref');
    
    my $query_config = "SELECT * FROM config";
    print Dumper $query_config if $CONFIG{'DEBUG'};
    my $response_config = &db_query($query_config, $connection, 'selectall_hashref', 'NAME');
    
    return ($response, $response_config);
}

sub accumulate_halls_data {
    &Delimiter((caller(0))[3]);
    my ($response) = @_;
    
    my %halls;
    
    for my $i (0 .. $#{$response}) {
        # print Dumper $$response[$i];
        
        my $hall_code = $$response[$i][3];
        $halls{$hall_code}{'VENUE_AGENCY'} = $$response[$i][0];
        $halls{$hall_code}{'VENUE_NAME'} = $$response[$i][1];
        $halls{$hall_code}{'HALL_NAME'} = $$response[$i][2];
        $halls{$hall_code}{'HALL_CODE'} = $$response[$i][3];
        $halls{$hall_code}{'HALL_FRONT_INDICATOR'} = $$response[$i][4];
        $halls{$hall_code}{'HALL_PADDING_LEFT'} = $$response[$i][5];
        $halls{$hall_code}{'HALL_PADDING_LEFT_STAGE'} = $$response[$i][6];
        $halls{$hall_code}{'HALL_MAX_TICKETS_PER_USER'} = $$response[$i][7];
        $halls{$hall_code}{'HALL_BUEHNE'} = $$response[$i][8];
        $halls{$hall_code}{'HALL_LEFT_NAMING'} = $$response[$i][9];
        $halls{$hall_code}{'HALL_TONPULT'} = $$response[$i][10];
        $halls{$hall_code}{'HALL_CSS'} = $$response[$i][11];
        $halls{$hall_code}{'HALL_DESC'} = $$response[$i][12];
        $halls{$hall_code}{'BUYER_TYPES'}{$$response[$i][13]} = $$response[$i][14];
        $halls{$hall_code}{'HALL_ID'} = $$response[$i][15];
        
        my $hall_id = $$response[0][15];
        my $query_color = "SELECT * FROM custom_pricescales_color WHERE HALL_ID = $halls{$hall_code}{'HALL_ID'}";
        print Dumper $query_color if $CONFIG{'DEBUG'};
        my $response_color = &db_query($query_color, $connection, 'selectall_hashref', 'ID');
        if (%$response_color) {
             $halls{$hall_code}{'CUSTOM_PRICESCALES_COLOR'} = $response_color;
        }
       
    };
    
    return \%halls;
}

sub convert_to_config_format {
    &Delimiter((caller(0))[3]);
    my ($halls_ref, $response_config) = @_;
    
    
    my %seatmap_config;
    for my $hall_code (keys %{$halls_ref}) {
        my $hall_name = $$halls_ref{$hall_code}{'HALL_NAME'};
        
        $seatmap_config{$hall_code} = # HALL CONFIG
        {
                    "FRONT-INDICATOR-WIDTH" => $$halls_ref{$hall_code}{'HALL_FRONT_INDICATOR'},
                    "PADDING-LEFT" => $$halls_ref{$hall_code}{'HALL_PADDING_LEFT'},
                    "PADDING-LEFT-STAGE" => $$halls_ref{$hall_code}{'HALL_PADDING_LEFT_STAGE'},
                    "MAX_TICKETS_PER_USER" => $$halls_ref{$hall_code}{'HALL_MAX_TICKETS_PER_USER'},
                    "BUEHNE" => $$halls_ref{$hall_code}{'HALL_BUEHNE'},
                    "LEFT_NAMING" => $$halls_ref{$hall_code}{'HALL_LEFT_NAMING'},
                    "CSS" => $$halls_ref{$hall_code}{'HALL_CSS'},
        };
        $seatmap_config{$hall_name} = # HALL BUYER TYPES
        {
                    "AGENCY" => $$halls_ref{$hall_code}{'VENUE_AGENCY'},
                    "BUYER_TYPES" => $$halls_ref{$hall_code}{'BUYER_TYPES'},
        };
        
        if ($$halls_ref{$hall_code}{'CUSTOM_PRICESCALES_COLOR'}) {
            for my $id (keys %{$$halls_ref{$hall_code}{'CUSTOM_PRICESCALES_COLOR'}}) {
                my $code = $$halls_ref{$hall_code}{'CUSTOM_PRICESCALES_COLOR'}{$id}{'CODE'};
                my $color = $$halls_ref{$hall_code}{'CUSTOM_PRICESCALES_COLOR'}{$id}{'COLOR'};
                $seatmap_config{$hall_code}{'CUSTOM_PRICESCALES_COLOR'}{$code} = $color;
            };
        }
        
    }
    
    for my $key (keys %$response_config) {
        if ($key eq 'CORS_ANYWHERE') {
            $seatmap_config{'CORS-ANYWHERE'}{'ROOT_URL'} = $$response_config{$key}{'VALUE'};
        }
        else {
            $seatmap_config{$key} = $$response_config{$key}{'VALUE'};
        }
    };
    
    
    return \%seatmap_config;
}

sub getVenueConfig {
    &Delimiter((caller(0))[3]);
    
    # CONNECT TO DB
    $connection = connectToMySql();
    
    # GET DB DATA FOR VENUE / HALLS / BUYER TYPES
    my ($response, $response_config) = db_getVenueConfig();
    
    # CREATE ONE DATASET AND ACCUMULATE ALL BUYER TYPES FOR HALL
    my $halls_ref = &accumulate_halls_data($response);
    # print Dumper $halls_ref;

    # CONVERT DATA TO STEAMAP CONFIG FORMAT
    my $seatmap_config_ref = convert_to_config_format($halls_ref, $response_config);
    
    # DISCONNECT FROM DB
    $connection->disconnect();
    
    return $seatmap_config_ref;
}

sub db_query {
    &Delimiter((caller(0))[3]); 
    my ($query, $connection, $switch, @rest) = @_;
    my $response;

    my $statement;
    if ($switch eq 'fetchrow') {
        $statement = $connection->prepare($query);
        $statement->execute();
        $response = $statement->fetchrow();
    }
    elsif ($switch eq 'fetchall_hashref') {
        $statement = $connection->prepare($query);
        $statement->execute();
        $response = $statement->fetchall_hashref($rest[0]);
    }
    elsif ($switch eq 'fetchall_arrayref') {
        $statement = $connection->prepare($query);
        $statement->execute();
        $response = $statement->fetchall_arrayref();
    }
    elsif ($switch eq 'do') {
        $response = $connection->do($query) or die $connection->errstr;
    }
    elsif ($switch eq 'selectall_hashref') {
        $response = $connection->selectall_hashref($query, $rest[0]) or die $connection->errstr;
    }
    elsif ($switch eq 'selectall_array') {
        my @response = $connection->selectall_array($query) or die $connection->errstr;
        return \@response;
    }
    elsif ($switch eq 'selectall_arrayref') {
        $response = $connection->selectall_arrayref($query) or die $connection->errstr;
    }
    elsif ($switch eq 'selectrow_hashref') {
        $response = $connection->selectrow_hashref($query) or die $connection->errstr;
    }
    $statement->finish if $statement;
    
    return $response;
}

sub connectToMySql {
    &Delimiter((caller(0))[3]); 
    
    my $connectionInfo = "dbi:mysql:$CONFIG{'MYSQL'}{'database'};$CONFIG{'MYSQL'}{'hostname'}";
    
    # make connection to database
    my $connection = DBI->connect($connectionInfo, $CONFIG{'MYSQL'}{'user'}, $CONFIG{'MYSQL'}{'pw'}, {
        RaiseError => 0, # SUGGESTED BY AnyEvent::DBI::MySQL
        AutoCommit => 1,
        mysql_auto_reconnect => 1,
    });
    return $connection;
}

sub wipeDataOverhead {
    &Delimiter((caller(0))[3]);
    
    delete $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{'AVAILABILITY'};
    for my $pricescaleID (keys(%{$DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}})) {
        delete $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$pricescaleID}{'SEATS'};
    }
}

sub printOutput {
    my $config_ref = shift;
    
    if (!$CONFIG{'DEBUG'}) {
        &wipeDataOverhead();
        # print header('application/json; charset=utf-8;'), encode_json $DATA{'COMPILED'};
        
        print header(
          -type => 'application/json',
          -access_control_allow_origin => '*',
          -charset => 'utf-8',
        );
        
        # FIX ENCODING
        $DATA{'COMPILED'} = Encode::Deep::decode('utf-8', Encode::Deep::encode('cp850', $DATA{'COMPILED'}));
        # my $encoded = Encode::Deep::encode('cp850', $DATA{'COMPILED'});
        # my $decoded = Encode::Deep::decode('utf-8', $encoded);
        
        # ADD SEATMAP CONFIG
        $DATA{'COMPILED'}{'CONFIG'} = $config_ref;
        
        # OUTPUT JSON
        print encode_json $DATA{'COMPILED'};
    }
    elsif ($CONFIG{'DEBUG'} && $CONFIG{'DEBUG_PRINT_DATA'}) {
        &Delimiter((caller(0))[3]);
        
        print "\n\n" . encode_json $DATA{'COMPILED'};
        # print "\n\n" . Dumper(\%DATA); 
        
    }
}

sub checkHost() {
    &Delimiter((caller(0))[3]);
    return if !$CONFIG{'ONLINE'};
    
    my $URI = URI->new($DATA{'PARAMETER'}{'url'}, 'http');
    if (($URI->host() eq 'purchase.tickets.com') && $URI->scheme && $DATA{'PARAMETER'}{'url'}) {
        print "VALID HOST\n" if $CONFIG{'DEBUG'};
    }
    else {
        die "INVALID OR MISSING HOST.\n";
    }
}

sub benchmarkEnd() {
    &Delimiter((caller(0))[3]);
    
    my $t1 = Benchmark->new;
    my $td = timediff($t1, $t0);
    print "BENCHMARK: ",timestr($td),"\n" if $CONFIG{'DEBUG'};
}

sub generateVenueUrl() {
    &Delimiter((caller(0))[3]);
    return if !$CONFIG{'ONLINE'};

    $DATA{'PARAMETER'}{'urlVenue'} = $DATA{'PARAMETER'}{'url'} . "&supplier_code=$DATA{'PARAMETER'}{'supplier_code'}&bots_event_code=$DATA{'PARAMETER'}{'bots_event_code'}&event_sub_code=$DATA{'PARAMETER'}{'event_sub_code'}" . "&holdcode=0&event=$DATA{'PARAMETER'}{'event'}&nocache=0&inclpkg=Y&incloffer=Y&inclcartdetails=Y&inclCart=Y&inclvenue=Y";
}

sub getSeatmaps() {
    &Delimiter((caller(0))[3]);
    
    for my $seatmapID (keys(%{$DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}})) {
        my $url = $DATA{'PARAMETER'}{'url'} . "&supplier_code=$DATA{'PARAMETER'}{'supplier_code'}&bots_event_code=$DATA{'PARAMETER'}{'bots_event_code'}&event_sub_code=$DATA{'PARAMETER'}{'event_sub_code'}" . "&event=$DATA{'PARAMETER'}{'event'}&nocache=1&inclsection=Y&incloffer=Y&offer=0&inclseatmap=Y&inclCart=Y&holdcode=0&seatmap=$seatmapID";
        
        print "GET SEATMAP $seatmapID...\n" if $CONFIG{'DEBUG'};
        &loadXML(get($url), \$DATA{'DOM'});
        parxeXML_section();
    }
}

sub parxeXML_section {
    &Delimiter((caller(0))[3]);
    
    my $id = $DATA{'DOM'}->findvalue('//seatmap/@id');
    #print "ID: $id\n";
    
    # CREATE PRICE ARRAYS
    foreach my $node ($DATA{'DOM'}->findnodes('//pricescale_config/pricescale')) {
        my $pricescaleID = $node->findvalue('@id');
        @{$DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$pricescaleID}{'SEATS'}} = split /,/, $node->findvalue('@mask');
    }
    
    # CREATE AVAILABILITY ARRAYS
    @{$DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{'AVAILABILITY'}{'AVAILABLE'}} = split /,/, $DATA{'DOM'}->findvalue('//availability/@available_selectable_mask');
    @{$DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{'AVAILABILITY'}{'UNAVAILABLE'}} = split /,/, $DATA{'DOM'}->findvalue('//availability/@unavailable_unselectable_mask');
    
    # CREATE SEATMAP_CONFIG ROWS
    my $rowCalculated = 0;
    foreach my $node ($DATA{'DOM'}->findnodes('//rows/row')) {
        $rowCalculated++;
        my @seats = split /\|/, $node->findvalue('@seats');
        my $yCoord = $node->findvalue('@y_cell_coord');
        #print Dumper(@seats);
        
        my $seatNR = 0;
        foreach my $i (0 .. $#seats) {
            $seatNR++;
            my ($id1, $id2, $number, $sectionID, $row, $seatNR_parsed) = split /,/, $seats[$i];
            #print "ID1: $id1 ID2: $id2 NUMBER: $number SECTIONID: $sectionID ROW: $rowCalculated SEATNR: $seatNR\n";
            
            $row =~ s/\s+$//;
            ($DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'ID1'}, $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'X_COORD'}, $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'SECTIONID'}, $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'ROW'}, $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'SEATNR'}, $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'Y_COORD'}, $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'ROW_PARSED'}, $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'SEATNR_PARSED'}) = ($id1, $number, $sectionID, $rowCalculated, $seatNR, $yCoord, $row, $seatNR_parsed);
            
            # SECTION DESC
            $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'SECTIONID_DESC'} = $DATA{'COMPILED'}{'VENUE'}{'SECTION_CONFIG'}{$sectionID}{'DESC'};
            
            # CHECK PRICE AND ADD COLOR
            for my $pricescaleID (keys(%{$DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}})) {
                if ( grep( /^$id1$/, @{$DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$pricescaleID}{'SEATS'}} ) ) {
                    #print "found it for price $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$pricescaleID}{'PRICES'}{'REF_PRICE'}\n";
                    $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'PRICESCALES_ID'} = $pricescaleID;
                    
                    #%{$DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'PRICES'}} = %{$DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$pricescaleID}{'PRICES'}};
                    #$DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'COLOR'} = $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$pricescaleID}{'COLOR'};
                }
            }
            
            # CHECK AVAILABILITY
            if ( grep( /^$id1$/, @{$DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{'AVAILABILITY'}{'AVAILABLE'}} ) ) {
                $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'AVAILABLE'} = 1;
                $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'ENABLED'} = 1;
            }
            elsif ( grep( /^$id1$/, @{$DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{'AVAILABILITY'}{'UNAVAILABLE'}} ) ) {
                $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'AVAILABLE'} = 0;
                $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'ENABLED'} = 1;
            }
            else {
                $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'AVAILABLE'} = 0;
                $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'ENABLED'} = 0;
            }
            
        }
    }
}

sub substituteUmlauts {
    &Delimiter((caller(0))[3]);
    my $string_ref = shift;
   
    $$string_ref =~ s/\x{251c}\x{255d}/ue/ if $$string_ref =~ /\x{251c}\x{255d}/;
    $$string_ref =~ s/\x{251c}\x{2562}/oe/ if $$string_ref =~ /\x{251c}\x{2562}/;
    $$string_ref =~ s/\x{251c}\x{f1}/ae/ if $$string_ref =~ /\x{251c}\x{f1}/;
    $$string_ref =~ s/\x{251c}\x{cd}/a/ if $$string_ref =~ /\x{251c}\x{cd}/;
    
    $$string_ref = uc($$string_ref);
}

sub fix_encoding() {
    my $string = shift;
    return decode('utf-8', encode('cp850', $string));
}

sub parseXML {
    &Delimiter((caller(0))[3]);
    
    # EVENT
    $DATA{'COMPILED'}{'EVENT'}{'ID'} = $DATA{'DOM'}->findvalue('//event/@id');
    $DATA{'COMPILED'}{'EVENT'}{'CODE'} = $DATA{'DOM'}->findvalue('//event/@code');
    $DATA{'COMPILED'}{'EVENT'}{'DESC'} = $DATA{'DOM'}->findvalue('//event/@desc');
    # $DATA{'COMPILED'}{'EVENT'}{'DESC'} = &fix_encoding($DATA{'COMPILED'}{'EVENT'}{'DESC'});
    
    # &substituteUmlauts(\$DATA{'COMPILED'}{'EVENT'}{'DESC'});
    $DATA{'COMPILED'}{'EVENT'}{'PUBLIC_DESC'} = $DATA{'DOM'}->findvalue('//event/@public_desc');
    # &substituteUmlauts(\$DATA{'COMPILED'}{'EVENT'}{'PUBLIC_DESC'});
    $DATA{'COMPILED'}{'EVENT'}{'YEAR'} = $DATA{'DOM'}->findvalue('//event/@year');
    $DATA{'COMPILED'}{'EVENT'}{'MONTH'} = $DATA{'DOM'}->findvalue('//event/@month');
    $DATA{'COMPILED'}{'EVENT'}{'DAY'} = $DATA{'DOM'}->findvalue('//event/@day');
    $DATA{'COMPILED'}{'EVENT'}{'HOUR'} = $DATA{'DOM'}->findvalue('//event/@hour');
    $DATA{'COMPILED'}{'EVENT'}{'MINUTE'} = $DATA{'DOM'}->findvalue('//event/@minute');
    $DATA{'COMPILED'}{'EVENT'}{'DATETIME_DE'} = "$DATA{'COMPILED'}{'EVENT'}{'DAY'}.$DATA{'COMPILED'}{'EVENT'}{'MONTH'}.$DATA{'COMPILED'}{'EVENT'}{'YEAR'} $DATA{'COMPILED'}{'EVENT'}{'HOUR'}:$DATA{'COMPILED'}{'EVENT'}{'MINUTE'}";
    
    # VENUE
    $DATA{'COMPILED'}{'VENUE'}{'CODE'} = $DATA{'DOM'}->findvalue('//venue/@code');
    $DATA{'COMPILED'}{'VENUE'}{'DESC'} = $DATA{'DOM'}->findvalue('//venue/@desc');
    $DATA{'COMPILED'}{'VENUE'}{'NAME'} = $DATA{'DOM'}->findvalue('//venue/@name');
    $DATA{'COMPILED'}{'VENUE'}{'ID'} = $DATA{'DOM'}->findvalue('//venue/@id');
    
    # VENUE PRICESCALES
    foreach my $node ($DATA{'DOM'}->findnodes('//pricescales/pricescale')) {
        my $id = $node->findvalue('@id');
        $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$id}{'ID'} = $id;
        $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$id}{'CODE'} = $node->findvalue('@code');
        $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$id}{'COLOR'} = $node->findvalue('@color');
        $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$id}{'DESC'} = $node->findvalue('@desc');
    }
    
    # VENUE BUYER TYPES
    foreach my $node ($DATA{'DOM'}->findnodes('//buyer_types/buyer_type')) {
        my $id = $node->findvalue('@id');
        $DATA{'COMPILED'}{'VENUE'}{'BUYER_TYPES'}{$id}{'ID'} = $id;
        $DATA{'COMPILED'}{'VENUE'}{'BUYER_TYPES'}{$id}{'CODE'} = $node->findvalue('@code');
        $DATA{'COMPILED'}{'VENUE'}{'BUYER_TYPES'}{$id}{'DESC'} = uc($node->findvalue('@desc'));
        # &substituteUmlauts(\$DATA{'COMPILED'}{'VENUE'}{'BUYER_TYPES'}{$id}{'DESC'});
        $DATA{'COMPILED'}{'VENUE'}{'BUYER_TYPES'}{$id}{'DISPLAY_INDICATOR'} = uc($node->findvalue('@desc'));
        # &substituteUmlauts(\$DATA{'COMPILED'}{'VENUE'}{'BUYER_TYPES'}{$id}{'DISPLAY_INDICATOR'});
    }
    
    # MASTER CONFIG TO VENUE
    $DATA{'COMPILED'}{'VENUE'}{'CAPACITY'} = $DATA{'DOM'}->findvalue('//master_config/@capacity');
    
    # MASTER SECTION CONFIG
    foreach my $node ($DATA{'DOM'}->findnodes('//master_config/section_config/section')) {
        my $id = $node->findvalue('@id');
        $DATA{'COMPILED'}{'VENUE'}{'SECTION_CONFIG'}{$id}{'ID'} = $id;
        $DATA{'COMPILED'}{'VENUE'}{'SECTION_CONFIG'}{$id}{'CODE'} = $node->findvalue('@code');
        $DATA{'COMPILED'}{'VENUE'}{'SECTION_CONFIG'}{$id}{'DESC'} = $node->findvalue('@desc');
    }
    
    # PRICE_STRUCTURE PRICESCALE
    foreach my $node ($DATA{'DOM'}->findnodes('//price_structure/pricescale')) {
        my $id = $node->findvalue('@id');
        
        $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$id}{'CURRENCY'} = $node->findvalue('@currency');
        $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$id}{'PRICES'}{'REF_PRICE'} = $node->findvalue('@ref_price');
        
        # BUYER TYPES
        foreach my $node_buyer ($node->findnodes('./buyer_type')) {
            my $id2 = $node_buyer->findvalue('@id');
            my $desc = $DATA{'COMPILED'}{'VENUE'}{'BUYER_TYPES'}{$id2}{'DESC'};
            my $price = $node_buyer->findvalue('@price');
            $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$id}{'PRICES'}{$desc} = $price;
        }
    }
    
    # SEATMAP_CONFIG
    foreach my $node ($DATA{'DOM'}->findnodes('//seatmap_config/seatmap')) {
        my $id = $node->findvalue('@id');
        
        $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ID'} = $id;
        $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'CODE'} = $node->findvalue('@code');
        $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'DESC'} = $node->findvalue('@desc');
        $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'HOTSPOT_COORDS'} = $node->findvalue('@hotspot_coords');
    }
    
    # MASTER SECTION INVENTORY
    foreach my $node ($DATA{'DOM'}->findnodes('//master_config/section_inventory/section')) {
        my $id = $node->findvalue('@id');
        
        $DATA{'COMPILED'}{'VENUE'}{'SECTION_CONFIG'}{$id}{'CAPACITY'} = $node->findvalue('@capacity');
        
        # PRICESCALE
        my $still_available = 0;
        foreach my $node_pricescale ($node->findnodes('./pricescale')) {
            my $id2 = $node_pricescale->findvalue('@id');
            my $available = $node_pricescale->findvalue('@available');
            $still_available += $available;

            my $price = $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$id2}{'PRICES'}{'REF_PRICE'};
            my $code = $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$id2}{'CODE'};
            
            
            $DATA{'COMPILED'}{'VENUE'}{'SECTION_CONFIG'}{$id}{'AVAILABLILITY'}{$code}{'PRICE'} = $price;
            $DATA{'COMPILED'}{'VENUE'}{'SECTION_CONFIG'}{$id}{'AVAILABLILITY'}{$code}{'AVAILABLE'} = $available;
        }
        $DATA{'COMPILED'}{'VENUE'}{'SECTION_CONFIG'}{$id}{'STILL_AVAILABLE'} = $still_available;
    }
}

sub loadXML {
    &Delimiter((caller(0))[3]);
    my ($xml, $DATA_ref) = @_;
   
    # DELETE IF DOM ALREADY PRESENT
    if (ref($$DATA_ref) eq 'XML::LibXML::Document') {
        print 'DELETING EXISTING ' . ref($$DATA_ref) . "\n" if $CONFIG{'DEBUG'};
        $$DATA_ref = ();
    }
    
    if (!$CONFIG{'ONLINE'}) {
        print "LOADING $xml...\n" if $CONFIG{'DEBUG'};
        $$DATA_ref = eval { XML::LibXML->load_xml(location => $xml) } or die "DIE: XML LOADING ERROR\n" . $@;
    }
    elsif ($CONFIG{'ONLINE'}) {
        print "LOADING XML...\n" if $CONFIG{'DEBUG'};
        $$DATA_ref = eval { XML::LibXML->load_xml(string => $xml) } or die "DIE: XML LOADING ERROR\n" . $@;
        # $$DATA_ref = eval { decode('utf-8', encode('cp850', XML::LibXML->load_xml(string => $xml) ) ) } or die "DIE: XML LOADING ERROR\n" . $@;
    }
}

sub getParameter {
    &Delimiter((caller(0))[3]);
    return if !$CONFIG{'ONLINE'};
    
    my $url = $ENV{QUERY_STRING};
    my $decoded = uri_decode($url);
    say "decoded: " . $decoded if ($CONFIG{'DEBUG'});
    
    
    my $uri = URI->new($decoded);
    %{$DATA{'PARAMETER'}} = $uri->query_form;
    #$DATA{'PARAMETER'}{'url'} = url_param('url');
    $DATA{'PARAMETER'}{'url'} = $decoded;
    $DATA{'PARAMETER'}{'url'} =~ s/^url=//;
    
    # Remove note, request_switch from decoded uri
    $DATA{'PARAMETER'}{'url'} =~ s/&note(=[^&]*)?|^note(=[^&]*)?&?//;
    $DATA{'PARAMETER'}{'url'} =~ s/&request_switch(=[^&]*)?|^request_switch(=[^&]*)?&?//;
}

sub get {
    &Delimiter((caller(0))[3]);
    my $url = shift;
    
    if ($CONFIG{'USE_CORSANYWHERE'}) {
        say 'Generating url for cors-anywhere...';
        my ($scheme, $auth, $path, $query, $frag) = uri_split($url);
        #say "scheme: $scheme\nauth: $auth\npath: $path\nquery: $query\nfrag: $frag";
        $url = 'https://cors.zinomedia.de/purchase.tickets.com:443' . $path . '?' . $query;
        say "cors-anywhere URL: $url";
    }
    
    if ($CONFIG{'ONLINE'}) {
        print "GET:\n$url\n" if $CONFIG{'DEBUG'};
        
        
        #my $response = $ua->get($url);
        my $response = $ua->get($url,
          'User-Agent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36',
          'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
          'Accept-Language' => 'de,en-US;q=0.7,en;q=0.3',
          'Host' => 'purchase.tickets.com',
        );
        
        print 'STATUS_LINE: ' . $response->status_line . "\n" if $CONFIG{'DEBUG'};
        if ($response->is_success) {
            return $response->decoded_content;
        }
        else {
            $response->code();
            $response->message();
            $response->status_line();
            say 'Get unsuccessful' if $CONFIG{'DEBUG'};
            die;
        }
    }
    elsif (!$CONFIG{'ONLINE'}) {
        print "SET AS OFFLINE: USING LOCAL XML\n" if $CONFIG{'DEBUG'};
    }

}

sub Delimiter {
    my $SubName = shift;
    print "\n" . "\n> SUB " . $SubName . "\n" . '-' x 80 . "\n"  if $CONFIG{'DEBUG'};
    #print "\n" . "-" x 80 . "\nSUB " . $SubName . "\n" . '-' x 80 . "\n";
}

exit;