#
# This file is part of the LibreOffice project.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# This file incorporates work covered by the following license notice:
#
#   Licensed to the Apache Software Foundation (ASF) under one or more
#   contributor license agreements. See the NOTICE file distributed
#   with this work for additional information regarding copyright
#   ownership. The ASF licenses this file to you under the Apache
#   License, Version 2.0 (the "License"); you may not use this file
#   except in compliance with the License. You may obtain a copy of
#   the License at http://www.apache.org/licenses/LICENSE-2.0 .
#

package installer::windows::directory;

use strict;
use warnings;

use installer::exiter;
use installer::files;
use installer::globals;
use installer::pathanalyzer;
use installer::windows::idtglobal;
use installer::windows::msiglobal;

##############################################################
# Collecting all directory trees in global hash
##############################################################

my @msistandarddirectorynames = qw(
   AdminToolsFolder
   AppDataFolder
   CommonAppDataFolder
   CommonFiles64Folder
   CommonFilesFolder
   DesktopFolder
   FavoritesFolder
   FontsFolder
   LocalAppDataFolder
   MyPicturesFolder
   NetHoodFolder
   PersonalFolder
   PrintHoodFolder
   ProgramFiles64Folder
   ProgramFilesFolder
   ProgramMenuFolder
   RecentFolder
   SendToFolder
   StartMenuFolder
   StartupFolder
   System16Folder
   System64Folder
   SystemFolder
   TempFolder
   TemplateFolder
   WindowsFolder
   WindowsVolume
);

sub collectdirectorytrees
{
    my ( $directoryref ) = @_;

    for ( my $i = 0; $i <= $#{$directoryref}; $i++ )
    {
        my $onedir = ${$directoryref}[$i];
        my $styles = "";
        if ( $onedir->{'Styles'} ) { $styles = $onedir->{'Styles'}; }

        if ( $styles ne "" )
        {
            foreach my $treestyle ( keys %installer::globals::treestyles )
            {
                if ( $styles =~ /\b$treestyle\b/ )
                {
                    my $hostname = $onedir->{'HostName'};
                    # -> hostname is the key, the style the value!
                    $installer::globals::hostnametreestyles{$hostname} = $treestyle;
                }
            }
        }
    }
}

##############################################################
# Overwriting global programfilesfolder, if required
##############################################################

sub overwrite_programfilesfolder
{
    my ( $allvariables ) = @_;

    if (( $allvariables->{'64BITPRODUCT'} ) && ( $allvariables->{'64BITPRODUCT'} == 1 ))
    {
        $installer::globals::programfilesfolder = "ProgramFiles64Folder";
    }
}

##############################################################
# Maximum length of directory name is 72.
# Taking care of underlines, which are the separator.
##############################################################

sub make_short_dir_version
{
    my ($longstring) = @_;

    my $shortstring = "";
    my $cutlength = 60;
    my $length = 5; # So the directory can still be recognized
    my $longstring_save = $longstring;

    # Splitting the string at each "underline" and allowing only
    # $length characters per directory name.
    # Checking also uniqueness and length.

    for my $onestring ( split /_\s*/, $longstring )
    {
        my $partstring = "";

        if ( $onestring =~ /\-/ )
        {
            for my $onelocalstring ( split /-\s*/, $onestring )
            {
                if ( length($onelocalstring) > $length ) {
                    $onelocalstring = substr($onelocalstring, 0, $length);
                }
                $partstring .= "-" . $onelocalstring;
            }
            $partstring =~ s/^\s*\-//;
        }
        else
        {
            if ( length($onestring) > $length ) {
                $partstring = substr($onestring, 0, $length);
            }
            else {
                $partstring = $onestring;
            }
        }

        $shortstring .= "_" . $partstring;
    }

    $shortstring =~ s/^\s*\_//;

    # Setting unique ID to each directory
    # No counter allowed, process must be absolute reproducible due to patch creation process.

    # chomp(my $id = `echo $longstring_save | md5sum | sed -e "s/ .*//g"`);  # Very, very slow
    # my $subid = substr($id, 0, 9); # taking only the first 9 digits

    my $subid = installer::windows::msiglobal::calculate_id($longstring_save, 9); # taking only the first 9 digits

    if ( length($shortstring) > $cutlength ) { $shortstring = substr($shortstring, 0, $cutlength); }

    $shortstring = $shortstring . "_" . $subid;

    return $shortstring;
}

##############################################################
# Adding unique directory names to the directory collection
##############################################################

my $already_checked_the_frigging_directories_for_uniqueness = 0;

sub create_unique_directorynames
{
    my ($directoryref, $allvariables) = @_;

    my %completedirhashstep1 = ();
    my %shortdirhash = ();
    my %shortdirhashreverse = ();
    my $infoline = "";

    for ( my $i = 0; $i <= $#{$directoryref}; $i++ )
    {
        my $onedir = ${$directoryref}[$i];
        my $uniquename = $onedir->{'HostName'};

        my $styles = "";
        if ( $onedir->{'Styles'} ) { $styles = $onedir->{'Styles'}; }

        $uniquename =~ s/^\s*//g;               # removing beginning white spaces
        $uniquename =~ s/\s*$//g;               # removing ending white spaces
        $uniquename =~ s/\s//g;                 # removing white spaces
        $uniquename =~ s/\_//g;                 # removing existing underlines
        $uniquename =~ s/\.//g;                 # removing dots in directoryname
        $uniquename =~ s/\Q$installer::globals::separator\E/\_/g;   # replacing slash and backslash with underline
        $uniquename =~ s/OpenOffice/OO/g;
        $uniquename =~ s/LibreOffice/LO/g;
        $uniquename =~ s/_registry/_rgy/g;
        $uniquename =~ s/_registration/_rgn/g;
        $uniquename =~ s/_extension/_ext/g;
        $uniquename =~ s/_frame/_frm/g;
        $uniquename =~ s/_table/_tbl/g;
        $uniquename =~ s/_chart/_crt/g;
        $uniquename =~ s/_plat-linux/_plx/g;

        # The names after this small changes must still be unique!
        if ( exists($completedirhashstep1{$uniquename}) ) { installer::exiter::exit_program("ERROR: Error in packaging process. Unallowed modification of directory name, not unique (step 1): \"$uniquename\".", "create_unique_directorynames"); }
        $completedirhashstep1{$uniquename} = 1;

        # Starting to make unique name for the parent and its directory
        my $originaluniquename = $uniquename;

        $uniquename = make_short_dir_version($uniquename);

        # Checking if the same directory already exists, but has another short version.
        if (( exists($shortdirhash{$originaluniquename}) ) && ( $shortdirhash{$originaluniquename} ne $uniquename )) { installer::exiter::exit_program("ERROR: Error in packaging process. Unallowed modification of directory name, not unique (step 2A): \"$uniquename\".", "create_unique_directorynames"); }

        # Also checking vice versa
        # Checking if the same short directory already exists, but has another long version.
        if (( exists($shortdirhashreverse{$uniquename}) ) && ( $shortdirhashreverse{$uniquename} ne $originaluniquename )) { installer::exiter::exit_program("ERROR: Error in packaging process. Unallowed modification of directory name, not unique (step 2B): \"$uniquename\".", "create_unique_directorynames"); }

        # Creating assignment from long to short directory names
        $shortdirhash{$originaluniquename} = $uniquename;
        $shortdirhashreverse{$uniquename} = $originaluniquename;

        # Important: The unique parent is generated from the string $originaluniquename (with the use of underlines).

        my $uniqueparentname = $originaluniquename;
        my $keepparent = 1;

        if ( $uniqueparentname =~ /^\s*(.*)\_(.*?)\s*$/ )   # the underline is now the separator
        {
            $uniqueparentname = $1;
            $keepparent = 0;
        }
        else
        {
            $uniqueparentname = $installer::globals::programfilesfolder;
            $keepparent = 1;
        }

        if ( $styles =~ /\bPROGRAMFILESFOLDER\b/ )
        {
            $uniqueparentname = $installer::globals::programfilesfolder;
            $keepparent = 1;
        }
        if ( $styles =~ /\bCOMMONFILESFOLDER\b/ )
        {
            $uniqueparentname = $installer::globals::commonfilesfolder;
            $keepparent = 1;
        }
        if ( $styles =~ /\bCOMMONAPPDATAFOLDER\b/ )
        {
            $uniqueparentname = $installer::globals::commonappdatafolder;
            $keepparent = 1;
        }
        if ( $styles =~ /\bLOCALAPPDATAFOLDER\b/ )
        {
            $uniqueparentname = $installer::globals::localappdatafolder;
            $keepparent = 1;
        }

        if ( $styles =~ /\bSHAREPOINTPATH\b/ )
        {
            $uniqueparentname = "SHAREPOINTPATH";
            $installer::globals::usesharepointpath = 1;
            $keepparent = 1;
        }

        # also setting short directory name for the parent

        my $originaluniqueparentname = $uniqueparentname;

        if ( ! $keepparent )
        {
            $uniqueparentname = make_short_dir_version($uniqueparentname);
        }

        # Again checking if the same directory already exists, but has another short version.
        if (( exists($shortdirhash{$originaluniqueparentname}) ) && ( $shortdirhash{$originaluniqueparentname} ne $uniqueparentname )) { installer::exiter::exit_program("ERROR: Error in packaging process. Unallowed modification of directory name, not unique (step 3A): \"$uniqueparentname\".", "create_unique_directorynames"); }

        # Also checking vice versa
        # Checking if the same short directory already exists, but has another long version.
        if (( exists($shortdirhashreverse{$uniqueparentname}) ) && ( $shortdirhashreverse{$uniqueparentname} ne $originaluniqueparentname )) { installer::exiter::exit_program("ERROR: Error in packaging process. Unallowed modification of directory name, not unique (step 3B): \"$uniqueparentname\".", "create_unique_directorynames"); }

        $shortdirhash{$originaluniqueparentname} = $uniqueparentname;
        $shortdirhashreverse{$uniqueparentname} = $originaluniqueparentname;

        # Hyphen not allowed in database
        $uniquename =~ s/\-/\_/g;           # making "-" to "_"
        $uniqueparentname =~ s/\-/\_/g;     # making "-" to "_"

        # And finally setting the values for the directories
        $onedir->{'uniquename'} = $uniquename;
        $onedir->{'uniqueparentname'} = $uniqueparentname;

        # setting the installlocation directory
        if ( $styles =~ /\bISINSTALLLOCATION\b/ )
        {
            if ( $installer::globals::installlocationdirectoryset ) { installer::exiter::exit_program("ERROR: Directory with flag ISINSTALLLOCATION already set: \"$installer::globals::installlocationdirectory\".", "create_unique_directorynames"); }
            $installer::globals::installlocationdirectory = $uniquename;
            $installer::globals::installlocationdirectoryset = 1;
        }
    }
}

#####################################################
# Adding ":." to selected default directory names
#####################################################

sub check_sourcedir_addon
{
    my ( $onedir, $allvariableshashref ) = @_;

    if (($installer::globals::languagepack) ||
        ($installer::globals::helppack) ||
        ($allvariableshashref->{'CHANGETARGETDIR'}))
    {
        my $sourcediraddon = "\:\.";
        $onedir->{'defaultdir'} = $onedir->{'defaultdir'} . $sourcediraddon;
    }

}

#####################################################
# The directory with the style ISINSTALLLOCATION
# will be replaced by INSTALLLOCATION
#####################################################

sub set_installlocation_directory
{
    my ( $directoryref, $allvariableshashref ) = @_;

    if ( ! $installer::globals::installlocationdirectoryset ) { installer::exiter::exit_program("ERROR: Directory with flag ISINSTALLLOCATION not set!", "set_installlocation_directory"); }

    for ( my $i = 0; $i <= $#{$directoryref}; $i++ )
    {
        my $onedir = ${$directoryref}[$i];

        if ( $onedir->{'uniquename'} eq $installer::globals::installlocationdirectory )
        {
            $onedir->{'uniquename'} = "INSTALLLOCATION";
            check_sourcedir_addon($onedir, $allvariableshashref);
        }

        if ( $onedir->{'uniquename'} eq $installer::globals::vendordirectory )
        {
            check_sourcedir_addon($onedir, $allvariableshashref);
        }

        if ( $onedir->{'uniqueparentname'} eq $installer::globals::installlocationdirectory )
        {
            $onedir->{'uniqueparentname'} = "INSTALLLOCATION";
        }
    }
}

#####################################################
# Getting the name of the top level directory. This
# can have only one letter
#####################################################

sub get_last_directory_name
{
    my ($completepathref) = @_;

    if ( $$completepathref =~ /^.*[\/\\](.+?)\s*$/ )
    {
        $$completepathref = $1;
    }
}

#####################################################
# Creating the defaultdir for the file Director.idt
#####################################################

sub create_defaultdir_directorynames
{
    my ($directoryref, $shortdirnamehashref) = @_;

    my @shortnames = ();
    if ( $installer::globals::updatedatabase ) { @shortnames = values(%{$shortdirnamehashref}); }
    elsif ( $installer::globals::prepare_winpatch ) { @shortnames = values(%installer::globals::saved83dirmapping); }

    for ( my $i = 0; $i <= $#{$directoryref}; $i++ )
    {
        my $onedir = ${$directoryref}[$i];
        my $hostname = $onedir->{'HostName'};

        $hostname =~ s/\Q$installer::globals::separator\E\s*$//;
        get_last_directory_name(\$hostname);
        my $uniquename = $onedir->{'uniquename'};
        my $shortstring;
        if (( $installer::globals::updatedatabase ) && ( exists($shortdirnamehashref->{$uniquename}) ))
        {
            $shortstring = $shortdirnamehashref->{$uniquename};
        }
        elsif (( $installer::globals::prepare_winpatch ) && ( exists($installer::globals::saved83dirmapping{$uniquename}) ))
        {
            $shortstring = $installer::globals::saved83dirmapping{$uniquename};
        }
        else
        {
            $shortstring = installer::windows::idtglobal::make_eight_three_conform($hostname, "dir", \@shortnames);
        }

        my $defaultdir;

        if ( $shortstring eq $hostname )
        {
            $defaultdir = $hostname;
        }
        else
        {
            $defaultdir = $shortstring . "|" . $hostname;
        }

        $onedir->{'defaultdir'} = $defaultdir;

        my $fontdir = "";
        if ( $onedir->{'Dir'} ) { $fontdir = $onedir->{'Dir'}; }

        my $fontdefaultdir = "";
        if ( $onedir->{'defaultdir'} ) { $fontdefaultdir = $onedir->{'defaultdir'}; }

        if (( $fontdir eq $installer::globals::fontsdirhostname ) && ( $fontdefaultdir eq $installer::globals::fontsdirhostname ))
        {
            $installer::globals::fontsdirname = $onedir->{'defaultdir'};
            $installer::globals::fontsdirparent = $onedir->{'uniqueparentname'};
        }
    }
}

###############################################
# Fill content into the directory table
###############################################

sub create_directorytable_from_collection
{
    my ($directorytableref, $directoryref) = @_;

    for ( my $i = 0; $i <= $#{$directoryref}; $i++ )
    {
        my $onedir = ${$directoryref}[$i];
        my $hostname = $onedir->{'HostName'};
        my $dir = "";

        if ( $onedir->{'Dir'} ) { $dir = $onedir->{'Dir'}; }

        if (( $dir eq "PREDEFINED_PROGDIR" ) && ( $hostname eq "" )) { next; }  # removing files from root directory

        my $oneline = $onedir->{'uniquename'} . "\t" . $onedir->{'uniqueparentname'} . "\t" . $onedir->{'defaultdir'} . "\n";

        push(@{$directorytableref}, $oneline);
    }
}

###############################################
# Defining the root installation structure
###############################################

sub add_root_directories
{
    my ($directorytableref, $allvariableshashref, $onelanguage) = @_;

    my $oneline = "";

    if (( ! $installer::globals::languagepack ) && ( ! $installer::globals::helppack ) && ( ! $allvariableshashref->{'DONTUSESTARTMENUFOLDER'} ))
    {
        my $productname;

    $productname = $allvariableshashref->{'PRODUCTNAME'};
        my $productversion = $allvariableshashref->{'PRODUCTVERSION'};
        my $baseproductversion = $productversion;

        if (( $installer::globals::prepare_winpatch ) && ( $allvariableshashref->{'BASEPRODUCTVERSION'} ))
        {
            $baseproductversion = $allvariableshashref->{'BASEPRODUCTVERSION'};  # for example "2.0" for OOo
        }

        my $realproductkey = $productname;
        my $productkey = $productname . " " . $baseproductversion;

        if (( $allvariableshashref->{'POSTVERSIONEXTENSION'} ) && ( ! $allvariableshashref->{'DONTUSEEXTENSIONINDEFAULTDIR'} ))
        {
            $productkey = $productkey . " " . $allvariableshashref->{'POSTVERSIONEXTENSION'};
            $realproductkey = $realproductkey . " " . $allvariableshashref->{'POSTVERSIONEXTENSION'};
        }
        if ( $allvariableshashref->{'NOVERSIONINDIRNAME'} )
        {
            $productkey = $productname;
            $realproductkey = $productname;
        }
        if ( $allvariableshashref->{'NOSPACEINDIRECTORYNAME'} )
        {
            $productkey =~ s/\ /\_/g;
            $realproductkey =~ s/\ /\_/g;
        }

        my $shortproductkey = installer::windows::idtglobal::make_eight_three_conform($productkey, "dir");      # third parameter not used
        $shortproductkey =~ s/\s/\_/g;                                  # changing empty space to underline

        $oneline = "$installer::globals::officemenufolder\t$installer::globals::programmenufolder\t$shortproductkey|$realproductkey\n";
        push(@{$directorytableref}, $oneline);
    }

    $oneline = "TARGETDIR\t\tSourceDir\n";
    push(@{$directorytableref}, $oneline);

    $oneline = "WindowsFolder\tTARGETDIR\tWindows\n";
    push(@{$directorytableref}, $oneline);

    $oneline = "$installer::globals::programfilesfolder\tTARGETDIR\t.\n";
    push(@{$directorytableref}, $oneline);

    $oneline = "$installer::globals::programmenufolder\tTARGETDIR\t.\n";
    push(@{$directorytableref}, $oneline);

    $oneline = "$installer::globals::startupfolder\tTARGETDIR\t.\n";
    push(@{$directorytableref}, $oneline);

    $oneline = "$installer::globals::desktopfolder\tTARGETDIR\t.\n";
    push(@{$directorytableref}, $oneline);

    $oneline = "$installer::globals::startmenufolder\tTARGETDIR\t.\n";
    push(@{$directorytableref}, $oneline);

    $oneline = "$installer::globals::commonfilesfolder\tTARGETDIR\t.\n";
    push(@{$directorytableref}, $oneline);

    $oneline = "$installer::globals::commonappdatafolder\tTARGETDIR\t.\n";
    push(@{$directorytableref}, $oneline);

    $oneline = "$installer::globals::localappdatafolder\tTARGETDIR\t.\n";
    push(@{$directorytableref}, $oneline);

    if ( $installer::globals::usesharepointpath )
    {
        $oneline = "SHAREPOINTPATH\tTARGETDIR\t.\n";
        push(@{$directorytableref}, $oneline);
    }

    my $localtemplatefoldername = $installer::globals::templatefoldername;
    my $directorytableentry = $localtemplatefoldername;
    my $shorttemplatefoldername = installer::windows::idtglobal::make_eight_three_conform($localtemplatefoldername, "dir");
    if ( $shorttemplatefoldername ne $localtemplatefoldername ) { $directorytableentry = "$shorttemplatefoldername|$localtemplatefoldername"; }
    $oneline = "$installer::globals::templatefolder\tTARGETDIR\t$directorytableentry\n";
    push(@{$directorytableref}, $oneline);

    if ( $installer::globals::fontsdirname )
    {
        $oneline = "$installer::globals::fontsfolder\t$installer::globals::fontsdirparent\t$installer::globals::fontsfoldername\:$installer::globals::fontsdirname\n";
    }
    else
    {
        $oneline = "$installer::globals::fontsfolder\tTARGETDIR\t$installer::globals::fontsfoldername\n";
    }

    push(@{$directorytableref}, $oneline);

}

###############################################
# Creating the file Director.idt dynamically
###############################################

sub create_directory_table
{
    my ($directoryref, $languagesarrayref, $basedir, $allvariableshashref, $shortdirnamehashref, $loggingdir) = @_;

    # Structure of the directory table:
    # Directory Directory_Parent DefaultDir
    # Directory is a unique identifier
    # Directory_Parent is the unique identifier of the parent
    # DefaultDir is .:APPLIC~1|Application Data with
    # Before ":" : [sourcedir]:[destdir] (not programmed yet)
    # After ":" : 8+3 and not 8+3 the destination directory name

    for ( my $m = 0; $m <= $#{$languagesarrayref}; $m++ )
    {
        my $onelanguage = ${$languagesarrayref}[$m];
        $installer::globals::installlocationdirectoryset = 0;

    my @directorytable = ();
    my $infoline;

    overwrite_programfilesfolder($allvariableshashref);
    create_unique_directorynames($directoryref, $allvariableshashref);
    $already_checked_the_frigging_directories_for_uniqueness++;
    create_defaultdir_directorynames($directoryref, $shortdirnamehashref);  # only destdir!
    set_installlocation_directory($directoryref, $allvariableshashref);
    installer::windows::idtglobal::write_idt_header(\@directorytable, "directory");
    add_root_directories(\@directorytable, $allvariableshashref, $onelanguage);
    create_directorytable_from_collection(\@directorytable, $directoryref);

    # Saving the file

    my $directorytablename = $basedir . $installer::globals::separator . "Director.idt" . "." . $onelanguage;
    installer::files::save_file($directorytablename ,\@directorytable);
    $infoline = "Created idt file: $directorytablename\n";
    push(@installer::globals::logfileinfo, $infoline);
    }
}

################################################
# Check if the string starts with another string
################################################

sub starts_with
{
    my ($first, $second) = @_;

    return substr($first, 0, length($second)) eq $second;
}

###############################################
# Check if the directory prefix is a standard
# directory name. If it is the case, then the
# standard directory name is returned in $var.
###############################################

sub has_standard_directory_prefix
{
    my ($dir, $var) = @_;

    for my $d (@msistandarddirectorynames) {
        if (starts_with($dir, $d) && $dir ne $d) {
            installer::logger::print_message("... match found: [$d]\n");
            ${$var} = $d;
            return 1;
        }
    }

    return 0;
}

1;
