/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2007-2015 Broad Institute
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.broad.igv.gs.dm;


import biz.source_code.base64Coder.Base64Coder;
import com.google.gson.*;
import org.apache.log4j.Logger;
import org.broad.igv.prefs.Constants;
import org.broad.igv.prefs.PreferencesManager;
import org.broad.igv.util.HttpUtils;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * Utility class for accessing the GenomeSpace data manager web service
 *
 * @author Jim Robinson
 * @date Aug 2, 2011
 */
public class DMUtils {

    private static Logger log = Logger.getLogger(DMUtils.class);
    private static final String UPLOAD_SERVICE = "uploadurl";
    public static final String DEFAULT_DIRECTORY = "defaultdirectory";
    public static final String PERSONAL_DIRECTORY = "personaldirectory";

    /**
     * Fetch the contents of the GenomeSpace directory.
     *
     * @param directoryURL
     * @return
     * @throws IOException
     */
    public static GSDirectoryListing getDirectoryListing(URL directoryURL) throws IOException{

        String str = HttpUtils.getInstance().getContentsAsJSON(directoryURL);
        JsonParser parser = new JsonParser();
        JsonObject obj = parser.parse(str).getAsJsonObject();

        JsonObject directory = obj.get("directory").getAsJsonObject();
        String dirUrlString = directory.get("url").getAsString();

        LinkedList<GSFileMetadata> elements = new LinkedList();
        if (obj.has("contents")) {
            Object c = obj.get("contents");
            List<JsonObject> contents = new ArrayList();
            if (c instanceof JsonObject) {
                contents.add((JsonObject) c);
            } else {
                JsonArray tmp = (JsonArray) c;
                int l = tmp.size();
                for (int i = 0; i < l; i++) {
                    contents.add((JsonObject) tmp.get(i));
                }
            }

            ArrayList<GSFileMetadata> dirElements = new ArrayList();
            ArrayList<GSFileMetadata> fileElements = new ArrayList();
            int contentsLength = contents.size();
            for (int i = 0; i < contentsLength; i++) {
                JsonObject o = contents.get(i);
                GSFileMetadata metaData = GSFileMetadata.deserializeElement(o);
                if (metaData.isDirectory()) {
                    dirElements.add(metaData);
                } else {
                    fileElements.add(metaData);
                }
            }

            elements.addAll(dirElements);
            elements.addAll(fileElements);
        }

        return new GSDirectoryListing(dirUrlString, elements);

    }


    /**
     * Upload a file to GenomeSpace.
     *
     * @param localFile
     * @param gsPath    the relative path in the users GenomeSpace account
     * @throws IOException
     * @throws URISyntaxException
     */
    public static void uploadFile(File localFile, String gsPath) throws IOException, URISyntaxException {

        byte[] md5 = computeMD5(localFile);
        String base64String = new String(Base64Coder.encode(md5));
        long contentLength = localFile.length();
        String contentType = "application/text";

        String tmp = PreferencesManager.getPreferences().get(Constants.GENOME_SPACE_DM_SERVER) + UPLOAD_SERVICE +
                gsPath + "?Content-Length=" + contentLength +
                "&Content-MD5=" + URLEncoder.encode(base64String, "UTF-8") + "&Content-Type=" + contentType;

        String uploadURL = HttpUtils.getInstance().getContentsAsJSON(new URL(tmp));

        Map<String, String> headers = new HashMap<String, String>();
        headers.put("Content-MD5", base64String);
        headers.put("Content-Length", String.valueOf(contentLength));
        headers.put("Content-Type", contentType);

        HttpUtils.getInstance().uploadGenomeSpaceFile(uploadURL, localFile, headers);
    }


    public static GSFileMetadata createDirectory(String putURL) throws IOException{

        JsonObject dirMeta = new JsonObject();

        dirMeta.add("isDirectory", new JsonPrimitive(true));
        System.out.println(dirMeta.toString());

        String body = "{\"isDirectory\":true}";
        String response = HttpUtils.getInstance().createGenomeSpaceDirectory(new URL(putURL), body);

        JsonParser parser = new JsonParser();
        JsonElement obj = parser.parse(response);
        return GSFileMetadata.deserializeElement(obj);

    }

    static void deleteFileOrDirectory(String delURL) throws IOException{
        HttpUtils.getInstance().delete(new URL(delURL));
    }


    /**
     * Compute the MD5 hash for the given file.
     *
     * @param file
     * @return
     * @throws IOException
     */
    public static byte[] computeMD5(File file) throws IOException {
        BufferedInputStream in = null;
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            in = new BufferedInputStream(new FileInputStream(file));
            int theByte = 0;
            while ((theByte = in.read()) != -1) {
                md.update((byte) theByte);
            }
            return md.digest();

        } catch (NoSuchAlgorithmException e) {
            // Should be impossible to get here
            log.error("Error creating MD5 digest");
            throw new RuntimeException(e);
        } finally {
            if (in != null) in.close();
        }


    }

    /**
     * Convert an array of bytes to a hex string.
     *
     * @param v
     * @return
     */
    public static String toHexString(byte[] v) {
        final String HEX_DIGITS = "0123456789abcdef";
        StringBuffer sb = new StringBuffer(v.length * 2);
        for (int i = 0; i < v.length; i++) {
            int b = v[i] & 0xFF;
            sb.append(HEX_DIGITS.charAt(b >>> 4)).append(HEX_DIGITS.charAt(b & 0xF));
        }
        return sb.toString();
    }


    public static void main(String[] args) throws IOException, URISyntaxException {
        final String testFile = "/Users/jrobinso/projects/igv/test/data/bed/Unigene.sample.bed";
        final File localFile = new File(testFile);
        uploadFile(localFile, "/users/test/Unigene.sample.bed");
        System.exit(-1);

        /*
        File file = new File(testFile);

        byte[] md5 = computeMD5(file);

        String hexString = toHexString(md5);
        System.out.println(hexString);

        String base64String = (new BASE64Encoder()).encode(md5);
        System.out.println(base64String);    */
    }

}
