package com.magefree.update;

import com.magefree.update.helpers.ChechsumHelper;
import com.magefree.update.helpers.FileHelper;

import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Scanner;

/**
 * Mage Updater for updating Mage based on metadata from remote server.
 *
 * @author Loki, noxx
 */
public class Updater {

    /**
     * URL to get metadata and files from.
     */
    private static final String URL_PREFIX = "http://download.magefree.com/update/";

    /**
     * Main. Application Entry Point.
     *
     * @param args No args are used.
     *
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        Updater m = new Updater();

        // check files on local machine
        HashMap<String, String> local = m.readLocalData();

        // request information for files on update server
        HashMap<String, String> remote = m.downloadAndParseUpdateData();

        // compare to find updated files
        List<String> downloadList = m.findUpdated(local, remote);
        downloadList.addAll(m.findNew(local, remote));

        // download and replace
        m.downloadAndUpdate(downloadList);

        // remove odd files
        List<String> removeList = m.findRemoved(local, remote);
        m.removeFiles(removeList);

        if (downloadList.isEmpty() && removeList.isEmpty()) {
            System.out.println("Already up-to-date.");
        }
    }

    /**
     * Gets lists of files on local machine.
     * For each such file an map's entry is created with path and checksum.
     *
     * @return
     * @throws Exception
     */
    public HashMap<String, String> readLocalData() throws Exception {
        HashMap<String, String> result = new HashMap<>();
        for (File f : findFiles()) {
            result.put(f.getPath().replaceAll("\\\\", "/"), ChechsumHelper.getSHA1Checksum(f.getPath()));
        }
        return result;
    }

    /**
     * Get required files.
     *
     * @return
     * @throws Exception
     */
    public List<File> findFiles() throws Exception {
        ArrayList<File> result = new ArrayList<>();
        result.addAll(FileHelper.findAllFilesInDir("mage-client/lib"));
        result.addAll(FileHelper.findAllFilesInDir("mage-client/plugins"));
        result.addAll(FileHelper.findAllFilesInDir("mage-server/lib"));
        result.addAll(FileHelper.findAllFilesInDir("mage-server/plugins"));
        return result;
    }

    /**
     * Downloads metadata from remote server getting checksums for files.
     * This information will be used to find out what files should be downloaded and replaced or removed locally.
     *
     * @return
     * @throws Exception
     */
    public HashMap<String, String> downloadAndParseUpdateData() throws Exception {
        HashMap<String, String> result = new HashMap<>();
        URL url = new URL(URL_PREFIX + "update-data.txt");
        URLConnection urlConnection = url.openConnection();
        urlConnection.connect();
        Scanner scanner = new Scanner(urlConnection.getInputStream());
        while (scanner.hasNextLine()) {
            String[] lines = scanner.nextLine().split("  ");
            if (lines.length == 2) {
                result.put(lines[1], lines[0]);
                System.out.println("jar " + lines[1] + ", checksum " + lines[0]);
            }
        }
        return result;
    }

    /**
     * Finds the list of files that have been updated and should be replaced.
     * The fact of being changed is determined based on checksum received from remote server.
     *
     * @param local List of local files with check sums to be compared with remote.
     * @param remote List of remove files with check sum to be compared with local.
     *
     * @return List of files to be replaced with newer versions.
     */
    public List<String> findUpdated(HashMap<String, String> local, HashMap<String, String> remote) {
        ArrayList<String> result = new ArrayList<>();
        for (String remoteFile : remote.keySet()) {
            if (local.containsKey(remoteFile)) {
                if (!local.get(remoteFile).equals(remote.get(remoteFile))) {
//                    System.out.println("jar need to be updated - " + remoteFile + " local: " + local.get(remoteFile) + ", remoteL " + remote.get(remoteFile));
                    result.add(remoteFile);
                }
            }
        }
        return result;
    }

    public List<String> findNew(HashMap<String, String> local, HashMap<String, String> remote) {
        ArrayList<String> result = new ArrayList<>();
        for (String remoteFile : remote.keySet()) {
            if (!local.containsKey(remoteFile)) {
                //System.out.println("new jar found - " + remoteFile);
                result.add(remoteFile);
            }
        }
        return result;
    }

    /**
     * Finds files that should be removed.
     *
     * @param local List of local files with check sums to be compared with remote.
     * @param remote List of remove files with check sum to be compared with local.
     *
     * @return List of files to be removed.
     */
    public List<String> findRemoved(HashMap<String, String> local, HashMap<String, String> remote) {
        ArrayList<String> result = new ArrayList<>();
        for (String localFile : local.keySet()) {
            if (!remote.containsKey(localFile)) {
                //System.out.println("deleted jar found - " + localFile);
                result.add(localFile);
            }
        }
        return result;
    }

    /**
     * Downloads files and updated them.
     *
     * @param downloadList
     * @throws IOException
     */
    public void downloadAndUpdate(List<String> downloadList) throws IOException {
        for (String filename : downloadList) {
            URL url = new URL(URL_PREFIX + filename);
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.connect();

            if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                FileHelper.downloadFile(filename, urlConnection);
            } else {
                System.out.println(filename + " error status : " + urlConnection.getResponseMessage());
            }
        }
    }

    /**
     * Removes files from the list.
     *
     * @param files
     */
    public void removeFiles(List<String> files) {
        FileHelper.removeFiles(files);
    }
}