// License: GPL. Copyright 2007 by Immanuel Scholz and others
package org.openstreetmap.josm.io;

import static org.openstreetmap.josm.tools.I18n.tr;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;

import javax.swing.JOptionPane;

import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.gui.PleaseWaitDialog;

/**
 * This DataReader reads directly from the REST API of the osm server.
 *
 * It supports plain text transfer as well as gzip or deflate encoded transfers;
 * if compressed transfers are unwanted, set property osm-server.use-compression
 * to false.
 *
 * @author imi
 */
public abstract class OsmServerReader extends OsmConnection {

    private OsmApi api = OsmApi.getOsmApi();

    /**
     * Open a connection to the given url and return a reader on the input stream
     * from that connection. In case of user cancel, return <code>null</code>.
     * @param urlStr The exact url to connect to.
     * @param pleaseWaitDlg
     * @return An reader reading the input stream (servers answer) or <code>null</code>.
     */
    protected InputStream getInputStream(String urlStr, PleaseWaitDialog pleaseWaitDlg) throws OsmTransferException  {
        api.initialize();
        urlStr = api.getBaseUrl() + urlStr;
        return getInputStreamRaw(urlStr, pleaseWaitDlg);
    }

    protected InputStream getInputStreamRaw(String urlStr, PleaseWaitDialog pleaseWaitDlg) throws OsmTransferException {
        URL url = null;
        try {
            url = new URL(urlStr);
        } catch(MalformedURLException e) {
            throw new OsmTransferException(e);
        }
        try {
            activeConnection = (HttpURLConnection)url.openConnection();
        } catch(Exception e) {
            throw new OsmTransferException(tr("Failed to open connection to API {0}", url.toExternalForm()), e);
        }
        if (cancel) {
            activeConnection.disconnect();
            return null;
        }

        if (Main.pref.getBoolean("osm-server.use-compression", true)) {
            activeConnection.setRequestProperty("Accept-Encoding", "gzip, deflate");
        }

        activeConnection.setConnectTimeout(15000);

        try {
            System.out.println("GET " + url);
            activeConnection.connect();
        } catch (Exception e) {
            throw new OsmTransferException(tr("Couldn't connect to the osm server. Please check your internet connection."), e);
        }
        try {
            if (isAuthCancelled() && activeConnection.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED)
                throw new OsmApiException(HttpURLConnection.HTTP_UNAUTHORIZED,null,null);

            if (activeConnection.getResponseCode() != HttpURLConnection.HTTP_OK) {
                String errorHeader = activeConnection.getHeaderField("Error");
                InputStream i = null;
                i = activeConnection.getErrorStream();
                StringBuilder errorBody = new StringBuilder();
                if (i != null) {
                    BufferedReader in = new BufferedReader(new InputStreamReader(i));
                    String s;
                    while((s = in.readLine()) != null) {
                        errorBody.append(s);
                        errorBody.append("\n");
                    }
                }

                throw new OsmApiException(activeConnection.getResponseCode(), errorHeader, errorBody.toString());
            }

            String encoding = activeConnection.getContentEncoding();
            InputStream inputStream = new ProgressInputStream(activeConnection, pleaseWaitDlg);
            if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
                inputStream = new GZIPInputStream(inputStream);
            }
            else if (encoding != null && encoding.equalsIgnoreCase("deflate")) {
                inputStream = new InflaterInputStream(inputStream, new Inflater(true));
            }
            return inputStream;
        } catch(Exception e) {
            if (e instanceof OsmTransferException)
                throw (OsmTransferException)e;
            else
                throw new OsmTransferException(e);

        }
    }

    public abstract DataSet parseOsm() throws OsmTransferException;

}
