package screenshotmaker;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.List;
import javax.mail.internet.InternetHeaders;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMultipart;

/**
 * A simple class that handles Flickr picture upload
 * @author  Jean-Francois Denise
 */
public class Flickr {
    public static class Content {
        byte[] content;
        String contentType;
    }
    private static String sign(String toSign) throws Exception {
        MessageDigest digest = MessageDigest.getInstance("MD5");
        digest.update(toSign.getBytes());
        byte[] md5sum = digest.digest();
        BigInteger bigInt = new BigInteger(1, md5sum);
        return bigInt.toString(16);
    }
    private static String constructToSign(String api_key,
            String secret, String auth_token) {
        return secret + "api_key" + api_key + "auth_token"
            + auth_token;
    }

     /**
     * Compute the upload picture content
     * @param api_key API  key
     * @param secret Your secret
     * @param auth_token Your auth token
     * @param tags Optional tags
     * @param file Image
     * @param type File type (JPG, ...)
     * @throws java.lang.Exception In case content creation fails.
     */
    public static Content computePhotoUploadContent(String api_key,
            String secret, String auth_token,
            String tags, File file, String type) throws Exception {
            List parts = new ArrayList();
        // Add API KEY
        MimeBodyPart part1 = new MimeBodyPart();
        part1.setDisposition("form-data; name=\"api_key\"");
        part1.setText(api_key);
        parts.add(part1);

        // Add Auth Token
        MimeBodyPart part2 = new MimeBodyPart();
        part2.setDisposition("form-data; name=\"auth_token\"");
        part2.setText(auth_token);
        parts.add(part2);

        // Add the photo
        InternetHeaders headers = new InternetHeaders();
        headers.addHeader("Content-Type", "image/" + type);
        InputStream in = new FileInputStream(file);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int i;
        byte[] buffer = new byte[1024];
        while ((i = in.read(buffer)) != -1) {
            out.write(buffer, 0, i);
        }
        in.close();
        byte[] result = out.toByteArray();
        MimeBodyPart partPhoto = new MimeBodyPart(headers, result);
        partPhoto.setDisposition("form-data; name=\"photo\"; filename=" +
                file.getName());
        parts.add(partPhoto);

        String signature = null;
        if(tags != null && tags.length() != 0) {
            MimeBodyPart partTags = new MimeBodyPart();
            partTags.setDisposition("form-data; name=\"tags\"");
            partTags.setText(tags);
            parts.add(partTags);
            //Sign with tags
            signature = sign(constructToSign(api_key,secret,auth_token)+"tags"
                    +tags);
        } else
            signature = sign(constructToSign(api_key,secret,auth_token));

        // Add Signature
        MimeBodyPart partSignature = new MimeBodyPart();
        partSignature.setDisposition("form-data; name=\"api_sig\"");
        partSignature.setText(signature);
        parts.add(partSignature);

        // Create the multipart
        MimeMultipart multiPart = new MimeMultipart("form-data");
        for(MimeBodyPart p : parts) {
            multiPart.addBodyPart(p);
        }
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        multiPart.writeTo(stream);
        
        Content c = new Content();
        c.content = stream.toByteArray();
        c.contentType = multiPart.getContentType();

        return c;
    }

    /**
     * Upload picture method. Relies on HttpURLConnection.
     * @param api_key API  key
     * @param secret Your secret
     * @param auth_token Your auth token
     * @param tags Optional tags
     * @param file Image
     * @param type File type (JPG, ...)
     * @throws java.lang.Exception In case upload fails.
     */
    public static void upload(String api_key, String secret, String auth_token,
            String tags, File file, String type) throws Exception {

        // Create Connection
        HttpURLConnection connection = (HttpURLConnection) 
                new URL("http://api.flickr.com/services/upload/").
                openConnection();
        connection.setUseCaches(false);
        connection.setDoInput(true);
        connection.setDoOutput(true);
        connection.setRequestMethod("POST");
        Content c = computePhotoUploadContent(api_key, secret, auth_token,
            tags, file, type);
        connection.addRequestProperty("Content-Type", c.contentType);
        connection.addRequestProperty("Content-Length",
                String.valueOf(c.content.length));
        connection.getOutputStream().write(c.content);
        connection.getOutputStream().close();
        int code = connection.getResponseCode();
        if (code > 300) {
            if (code == 404) {
                throw new Exception("Error 404");
            }
            BufferedReader err = new BufferedReader(
                    new InputStreamReader(connection.getErrorStream()));
            String ret;
            StringBuffer buff = new StringBuffer();
            while ((ret = err.readLine()) != null) {
                buff.append(ret);
            }
            throw new IOException(buff.toString());
        } else {
            BufferedReader inp = new BufferedReader(
                    new InputStreamReader(connection.getInputStream()));
            StringBuffer buff = new StringBuffer();
            String ret;
            while ((ret = inp.readLine()) != null) {
                buff.append(ret);
            }
            
            if(buff.toString().contains("fail"))
                throw new Exception("Upload failed");
        }
    }
}