/**************************************************************
 * 
 * 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
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



package org.openoffice.setup.InstallerHelper;

import org.openoffice.setup.InstallData;
import org.openoffice.setup.SetupData.PackageDescription;
import org.openoffice.setup.Util.Converter;
import org.openoffice.setup.Util.ExecuteProcess;
import org.openoffice.setup.Util.LogManager;
import org.openoffice.setup.Util.SystemManager;
import java.io.File;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Vector;public class LinuxHelper {
    
    public LinuxHelper() {
        super();
    }

    private void getPackageNamesContent(PackageDescription packageData, Vector packageNames) {
        if (( packageData.getPackageName() != null ) && ( ! packageData.getPackageName().equals("")))  {
            packageNames.add(packageData.getPackageName() + "=" + packageData.getFullPackageName());
        }

        for (Enumeration e = packageData.children(); e.hasMoreElements(); ) {
            PackageDescription child = (PackageDescription) e.nextElement();
            getPackageNamesContent(child, packageNames);
        }
    }

    private String getPackageNameFromRpm(PackageDescription packageData, InstallData installData) {
        String fullPackageName = null;
        String packagePath = installData.getPackagePath();

        if (( packageData.getPkgSubdir() != null ) && ( ! packageData.getPkgSubdir().equals("") )) {
            File completePackageFile = new File(packagePath, packageData.getPkgSubdir());
            packagePath = completePackageFile.getPath();
        }

        String rpmFileName = packageData.getPackageName();
        File rpmFile = new File(packagePath, rpmFileName);

        if ( rpmFile.exists() ) {
            String rpmCommand = "rpm -qp " + rpmFile.getPath();
            String[] rpmCommandArray = new String[3];
            rpmCommandArray[0] = "rpm";
            rpmCommandArray[1] = "-qp";
            rpmCommandArray[2] = rpmFile.getPath();

            Vector returnVector = new Vector();
            Vector returnErrorVector = new Vector();
            int returnValue = ExecuteProcess.executeProcessReturnVector(rpmCommandArray, returnVector, returnErrorVector);
            String returnString = (String) returnVector.get(0);

            String log = rpmCommand + "<br><b>Returns: " + returnString + "</b><br>";
            LogManager.addCommandsLogfileComment(log);

            fullPackageName = returnString;

        } else {
            System.err.println("Error: Could not find file " + rpmFile.getPath());
        }

        return fullPackageName;
    }

    private boolean checkPackageExistence(PackageDescription packageData, InstallData installData) {
        boolean fileExists = false;

        String packagePath = installData.getPackagePath();

        if (( packageData.getPkgSubdir() != null ) && ( ! packageData.getPkgSubdir().equals("") )) {
            File completePackageFile = new File(packagePath, packageData.getPkgSubdir());
            packagePath = completePackageFile.getPath();
        }

        String rpmFileName = packageData.getPackageName();

        File rpmFile = new File(packagePath, rpmFileName);
        if ( rpmFile.exists() ) {
            fileExists = true;
        }

        return fileExists;
    }

    private HashMap analyzeVersionString(String versionString) {
        
        boolean errorOccured = false;
        
        Integer micro = null;
        Integer minor = null;
        Integer major = null;
        Integer release = null;
        
        String microString = null;
        String minorString = null;
        String majorString = null;
        String releaseString = null;
        
        int pos = versionString.lastIndexOf("_");  // this is a jre RPM (1.5.0_06)
        
        if ( pos > -1 ) {
            try {
                releaseString = versionString.substring(pos+1, versionString.length());
                versionString = versionString.substring(0, pos);
            } catch (IndexOutOfBoundsException ex) {
                System.err.println("Error: Could not get substring from " + versionString);
                errorOccured = true;
            }
            try {
                int releaseInt = Integer.parseInt(releaseString);
                release = new Integer(releaseInt);
            } catch (NumberFormatException ex) {
                System.err.println("Error: Could not convert " + releaseString + " to integer");                            
                errorOccured = true;
            }
        }

        // Problem: Some rpms have "2.3" instead of "2.3.0"
        String compareString = versionString;
        pos = compareString.lastIndexOf(".");  // returns "-1", if not found
        if ( pos > -1 ) {
            String substring = compareString.substring(0, pos);
            pos = substring.lastIndexOf(".");  // returns "-1", if not found
            if ( pos == -1 ) {
                versionString = versionString + ".0";
                // System.err.println("Warning: Changing from " + compareString + " to " + versionString);            
            }
        } else {
            versionString = versionString + ".0.0";            
        }
                    
        // the standard analyzing mechanism
        pos = versionString.lastIndexOf(".");  // returns "-1", if not found
        
        if ( pos > -1 )
        {
            try {
                microString = versionString.substring(pos+1, versionString.length());
                versionString = versionString.substring(0, pos);
            } catch (IndexOutOfBoundsException ex) {
                System.err.println("Error: Could not get substring from " + versionString);            
                errorOccured = true;                
            }

            pos = versionString.lastIndexOf(".");
            if ( pos > -1 ) {
                try {
                    minorString = versionString.substring(pos+1, versionString.length());
                    majorString = versionString.substring(0, pos);
                } catch (IndexOutOfBoundsException ex) {
                    System.err.println("Error: Could not get substring from " + versionString);            
                    errorOccured = true;
                }
                try {
                    int microInt = Integer.parseInt(microString);
                    int minorInt = Integer.parseInt(minorString);
                    int majorInt = Integer.parseInt(majorString);
                    micro = new Integer(microInt);
                    minor = new Integer(minorInt);
                    major = new Integer(majorInt);
                } catch (NumberFormatException ex) {
                    System.err.println("Error: Could not convert " + microString + "," + 
                                       minorString + " or " + majorString + " to integer");                            
                    errorOccured = true;
                }
            }
        }
          
        // if ( microString == null ) { microString = ""; }
        // if ( majorString == null ) { majorString = ""; }
        // if ( releaseString == null ) { releaseString = ""; }
        // if ( minorString == null ) { minorString = ""; }
        // System.err.println("Major " + majorString + " Minor: " + minorString + " Micro: " + microString + " Release: " + releaseString);
        
        if ( errorOccured ) {
            micro = null;
            minor = null;
            major = null;
            release = null;
        }
        
        HashMap hashRpm = new HashMap();
        
        hashRpm.put("micro", micro);
        hashRpm.put("minor", minor);
        hashRpm.put("major", major);
        hashRpm.put("release", release);
        
        // If one of this values is "null", procedure "compareTwoRpms" always delivers false.
        // This means, that the installed package is not older.
        
        // System.err.println("Analyzed: " + "micro: " + hashRpm.get("micro").toString() + " minor: " + hashRpm.get("minor").toString() + " major: " + hashRpm.get("major").toString());

        return hashRpm;
    }

    private HashMap analyzeReleaseString(HashMap hashRpm, String releaseString) {
        int release;
        
        try {
            release = Integer.parseInt(releaseString);
            Integer releaseObj = new Integer(release);
            hashRpm.put("release", releaseObj);
         }
        catch (NumberFormatException ex) {
        	// JRE often contain a string like "FCS"
            // System.err.println("Error: Could not convert " + releaseString + " to integer");
            hashRpm.put("release", null);
        } 

        return hashRpm;
    }

    public int getInstalledMinor(String version) {
    
        int minor = 0;
        int pos = version.indexOf(".");
        if ( pos > -1 ) {
            String reduced = version.substring(pos + 1, version.length());

            pos = reduced.indexOf(".");
            if ( pos > -1 ) {
                reduced = reduced.substring(0, pos);
                minor = Integer.parseInt(reduced);
            }
        }    	

    	return minor;
    }
    
    private boolean compareTwoRpms(HashMap hash1, HashMap hash2) {
        boolean hash1IsOlder = false;
        
        if (( hash1.get("major") != null ) && ( hash2.get("major") != null )) {
            if ( ((Integer)hash1.get("major")).intValue() < ((Integer)hash2.get("major")).intValue() ) {
                hash1IsOlder = true;
            } else {
                if (( hash1.get("minor") != null ) && ( hash2.get("minor") != null )) {
                    if ( ((Integer)hash1.get("minor")).intValue() < ((Integer)hash2.get("minor")).intValue() ) {
                        hash1IsOlder = true;
                    } else {
                        if (( hash1.get("micro") != null ) && ( hash2.get("micro") != null )) {
                            if ( ((Integer)hash1.get("micro")).intValue() < ((Integer)hash2.get("micro")).intValue() ) {
                                hash1IsOlder = true;
                            } else {
                                if (( hash1.get("release") != null ) && ( hash2.get("release") != null )) {
                                    if ( ((Integer)hash1.get("release")).intValue() < ((Integer)hash2.get("release")).intValue() ) {
                                        hash1IsOlder = true;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        return hash1IsOlder;
    }
    
    public boolean compareVersionAndRelease(String versionString, String releaseString, PackageDescription packageData, boolean checkIfInstalledIsOlder) {
        // version and release are gotten from the rpm database. packageData contains
        // the information about the rpm, that shall be installed. It has to be installed,
        // if the installed product defined by version and release is older.
        // version is something like "2.0.3", release something like "164". 
        // An exception is the jre package, where version is "1.5.0_06" and release "fcs".

        HashMap installedRpm = analyzeVersionString(versionString);
        if ( installedRpm.get("release") == null ) {
            installedRpm = analyzeReleaseString(installedRpm, releaseString);
        }
        
        // System.err.println("Package: " + packageData.getPackageName());
        // String outputString = "Installed RPM: ";
        // if ( installedRpm.get("major") != null ) { outputString = outputString + " major: " + installedRpm.get("major").toString(); }
        // else { outputString = outputString + " major is null"; }
        // if ( installedRpm.get("minor") != null ) { outputString = outputString + " minor: " + installedRpm.get("minor").toString(); }
        // else { outputString = outputString + " minor is null"; }
        // if ( installedRpm.get("micro") != null ) { outputString = outputString + " micro: " + installedRpm.get("micro").toString(); }
        // else { outputString = outputString + " micro is null"; }
        // if ( installedRpm.get("release") != null ) { outputString = outputString + " release: " + installedRpm.get("release").toString(); }
        // else { outputString = outputString + " release is null"; }
        // System.err.println(outputString);

        HashMap notInstalledRpm = analyzeVersionString(packageData.getPkgVersion());
        if ( notInstalledRpm.get("release") == null ) {
            notInstalledRpm = analyzeReleaseString(notInstalledRpm, packageData.getPkgRelease());
        }

        // outputString = "Not installed RPM: ";
        // if ( notInstalledRpm.get("major") != null ) { outputString = outputString + " major: " + notInstalledRpm.get("major").toString(); }
        // else { outputString = outputString + " major is null"; }
        // if ( notInstalledRpm.get("minor") != null ) { outputString = outputString + " minor: " + notInstalledRpm.get("minor").toString(); }
        // else { outputString = outputString + " minor is null"; }
        // if ( notInstalledRpm.get("micro") != null ) { outputString = outputString + " micro: " + notInstalledRpm.get("micro").toString(); }
        // else { outputString = outputString + " micro is null"; }
        // if ( notInstalledRpm.get("release") != null ) { outputString = outputString + " release: " + notInstalledRpm.get("release").toString(); }
        // else { outputString = outputString + " release is null"; }
        // System.err.println(outputString);

        boolean firstIsOlder = false;
        
        if ( checkIfInstalledIsOlder ) {
            firstIsOlder = compareTwoRpms(installedRpm, notInstalledRpm);
            // System.err.println("Result: Installed RPM is older: " + firstIsOlder);
        } else {
            firstIsOlder = compareTwoRpms(notInstalledRpm, installedRpm);        
            // System.err.println("Result: Not installed RPM is older: " + firstIsOlder);
        }
        
        return firstIsOlder;
    }

    public void getLinuxPackageNamesFromRpmquery(PackageDescription packageData, InstallData installData) {

        if ((packageData.getPackageName() != null) && ( ! packageData.getPackageName().equals(""))) {

            boolean rpmExists = checkPackageExistence(packageData, installData);

            if ( rpmExists ) {
                // Full package name not defined in xpd file
                if (( packageData.getFullPackageName() == null ) || ( packageData.getFullPackageName().equals(""))) {
                    // Now it is possible to query the rpm database for the packageName, if it is not defined in xpd file!
                    String fullPackageName = getPackageNameFromRpm(packageData, installData);
                    if ( fullPackageName != null ) {
                        packageData.setFullPackageName(fullPackageName);
                    } else {
                        System.err.println("Error: Linux package name not defined in xpd file and could not be determined: "
                                + packageData.getPackageName());
                    }
                }
                packageData.setPkgExists(true);

            } else {
                packageData.setPkgExists(false);
            }
            
        }

        for (Enumeration e = packageData.children(); e.hasMoreElements(); ) {
            PackageDescription child = (PackageDescription) e.nextElement();
            getLinuxPackageNamesFromRpmquery(child, installData);
        }
    }

    public String getLinuxDatabasePath(InstallData data) {
        String databasePath = null;
        String installDir = data.getInstallDir();
        String databaseDir = installDir;
        // String databaseDir = SystemManager.getParentDirectory(installDir);
        String linuxDatabaseName = ".RPM_OFFICE_DATABASE";
        File databaseFile = new File(databaseDir, linuxDatabaseName);
        databasePath = databaseFile.getPath();        
        return databasePath;
    }
  
    public void investigateDebian(InstallData data) {
        
        // First check: Is this a Debian system?
        
        String dpkgFile = "/usr/bin/dpkg";
        
        if ( new File(dpkgFile).exists() ) {
        	
            data.setIsDebianSystem(true);
        
            // Second check: If this is a Debian system, is "--force-debian" required? Older
            // versions do not support "--force-debian".
        
            // String rpmQuery = "rpm --help;
            String[] rpmQueryArray = new String[2];
            rpmQueryArray[0] = "rpm";
            rpmQueryArray[1] = "--help";

            Vector returnVector = new Vector();
            Vector returnErrorVector = new Vector();
            int returnValue = ExecuteProcess.executeProcessReturnVector(rpmQueryArray, returnVector, returnErrorVector);
        
            // Checking if the return vector contains the string "force-debian"
       
            for (int i = 0; i < returnVector.size(); i++) {
                String line = (String) returnVector.get(i);
                if ( line.indexOf("force-debian") > -1 ) {
                    data.setUseForceDebian(true);
                }
            }
        }
    } 
    
    public void getLinuxFileInfo(PackageDescription packageData) {
        // analyzing a string like "openoffice-core01-2.0.3-159" as "name-version-release"
        InstallData data = InstallData.getInstance();
        if ( packageData.pkgExists() ) {
            if (( packageData.getFullPackageName() != null ) && ( ! packageData.getFullPackageName().equals(""))) {
                String longName = packageData.getFullPackageName();

                int pos = longName.lastIndexOf("-");
                if (data.isInstallationMode()) {
                    // not saving at uninstallation, because it can be updated without GUI installer
                    packageData.setPkgRelease(longName.substring(pos+1, longName.length()));
                }
                longName = longName.substring(0, pos);

                pos = longName.lastIndexOf("-");
                if (data.isInstallationMode()) {
                    // not saving at uninstallation, because it can be updated without GUI installer
                    packageData.setPkgVersion(longName.substring(pos+1, longName.length()));
                }
                packageData.setPkgRealName(longName.substring(0, pos));
            }
        }

        for (Enumeration e = packageData.children(); e.hasMoreElements(); ) {
            PackageDescription child = (PackageDescription) e.nextElement();
            getLinuxFileInfo(child);
        }
        
    }

    public void setFullPackageNameAtUninstall(PackageDescription packageData, HashMap packageNames) {

        if (( packageData.getPackageName() != null ) && ( ! packageData.getPackageName().equals("")))  {
            if (( packageData.getFullPackageName() == null ) || ( packageData.getFullPackageName().equals(""))) {
                String packageName = packageData.getPackageName();
                // Does this always exist? Should not be required!
                // But is there another way to get the packageNames, without this file?
                // During installation the packageNames can be determined by querying the rpm file
                // -> this is not possible during uninstallation
                String fullPackageName = (String) packageNames.get(packageName);
                packageData.setFullPackageName(fullPackageName);
            }
        }

        for (Enumeration e = packageData.children(); e.hasMoreElements(); ) {
            PackageDescription child = (PackageDescription) e.nextElement();
            setFullPackageNameAtUninstall(child, packageNames);
        }
    }

    public String getRelocationString(PackageDescription packageData, String packageName) {
        String relocationString = null;
        
        if ( packageData.isRelocatable() ) {
            // String rpmQuery = "rpm -qp --qf %{PREFIXES}" + " " + packageName;
            String[] rpmQueryArray = new String[5];
            rpmQueryArray[0] = "rpm";
            rpmQueryArray[1] = "-qp";
            rpmQueryArray[2] = "--qf";
            rpmQueryArray[3] = "%{PREFIXES}";
            rpmQueryArray[4] = packageName;
            
            Vector returnVector = new Vector();
            Vector returnErrorVector = new Vector();
            int returnValue = ExecuteProcess.executeProcessReturnVector(rpmQueryArray, returnVector, returnErrorVector);
            relocationString = (String) returnVector.get(0);
        }
        
        return relocationString;
    }

    public void createPackageNameFileAtPostinstall(InstallData data, PackageDescription packageData) {
 
        // The file "packageNames" must not be an own database! It must be possible to install
        // and deinstall RPMs without this GUI installer. Therefore the file packageNames is
        // not always up to date. Nevertheless it makes the deinstallation faster, because of
        // all packages, whose "real" package name is not defined in xpd files (for example
        // "openoffice-core01-2.0.3-159.rpm" hat the "real" name "openoffice-core01" that is 
        // used for deinstallation) this can be read in this file. Otherwise it would be 
        // neccessary to determine the "real" name with a database question.
        // The version and release that are also stored in file "packageNames" must not be 
        // used for deinstallation because they are probably not up to date.
        
        File destDir = new File(data.getInstallDefaultDir(), data.getProductDir());      
        File uninstallDir = new File(destDir, data.getUninstallDirName());
        String fileName = "packageNames";        
        File packageNamesFile = new File(uninstallDir, fileName);        
        Vector packageNames = new Vector();
        getPackageNamesContent(packageData, packageNames);
        SystemManager.saveCharFileVector(packageNamesFile.getPath(), packageNames);
    }

    public HashMap readPackageNamesFile() {
        // package names are stored in file "packageNames" in data.getInfoRoot() directory
        String fileName = "packageNames";
        InstallData data = InstallData.getInstance();
        File dir = data.getInfoRoot();
        File file = new File(dir, fileName);
        Vector fileContent = SystemManager.readCharFileVector(file.getPath());
        HashMap map = Converter.convertVectorToHashmap(fileContent);
        return map;
    }
    
    public void saveModulesLogFile(InstallData data) {
        if ( data.logModuleStates() ) {
            Vector logContent = LogManager.getModulesLogFile();
            File destDir = new File(data.getInstallDefaultDir(), data.getProductDir());      
            File uninstallDir = new File(destDir, data.getUninstallDirName());
            File modulesLogFile = new File(uninstallDir, "moduleSettingsLog.txt");        
            SystemManager.saveCharFileVector(modulesLogFile.getPath(), logContent);    
        }    
    }

    public String fixInstallationDirectory(String installDir) {
        // inject a second slash to the last path segment to avoid rpm 3 concatenation bug
        int lastSlashPos = installDir.lastIndexOf('/');
        String sub1 = installDir.substring(0,lastSlashPos);
        String sub2 = installDir.substring(lastSlashPos);
        String fixedInstallDir = sub1 + "/" + sub2;
        // fixedInstallDir.replaceAll(" ", "%20");
        return fixedInstallDir;
    }

}
