/*
 * Copyright 2015 Hippo Seven
 *
 * Licensed 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 com.hippo.network;

import android.text.TextUtils;
import android.webkit.MimeTypeMap;

import com.hippo.io.UniFileOutputStreamPipe;
import com.hippo.unifile.UniFile;
import com.hippo.yorozuya.FileUtils;
import com.hippo.yorozuya.StringUtils;
import com.hippo.yorozuya.Utilities;
import com.hippo.yorozuya.io.OutputStreamPipe;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;

public class DownloadClient {

    private static long transferData(InputStream in, OutputStream out, OnDownloadListener listener)
            throws Exception {
        final byte data[] = new byte[1024 * 4];
        long receivedSize = 0;

        while (true) {
            int bytesRead = in.read(data);
            if (bytesRead == -1) {
                break;
            }
            out.write(data, 0, bytesRead);
            receivedSize += bytesRead;
            if (listener != null) {
                listener.onDonwlad(receivedSize, bytesRead);
            }
        }

        out.flush();

        return receivedSize;
    }

    private static String getFilenameFromContentDisposition(String contentDisposition) {
        if (contentDisposition == null) {
            return null;
        }

        String[] pieces = StringUtils.split(contentDisposition, ';');
        for (String piece : pieces) {
            int index = piece.indexOf('=');
            if (index < 0) {
                continue;
            }
            String key = piece.substring(0, index).trim();
            String value = piece.substring(index + 1).trim();
            if ("filename".equals(key) && !TextUtils.isEmpty(value)) {
                return value;
            }
        }

        return null;
    }

    public static boolean execute(DownloadRequest request) {
        OnDownloadListener listener = request.mListener;
        OkHttpClient okHttpClient = request.mOkHttpClient;

        UniFile uniFile = null;
        OutputStreamPipe osPipe = null;
        try {
            Call call = okHttpClient.newCall(new Request.Builder().url(request.mUrl).build());
            request.mCall = call;

            // Listener
            if (listener != null) {
                listener.onStartDownloading();
            }

            Response response = call.execute();
            ResponseBody body = response.body();

            // Check response code
            int responseCode = response.code();
            if (responseCode >= 400) {
                throw new ResponseCodeException(responseCode);
            }

            osPipe = request.mOSPipe;
            if (osPipe == null) {
                String extension;
                String name;

                String dispositionFilename = getFilenameFromContentDisposition(response.header("Content-Disposition"));
                if (dispositionFilename != null) {
                    name = FileUtils.getNameFromFilename(dispositionFilename);
                    extension = FileUtils.getExtensionFromFilename(dispositionFilename);
                } else {
                    name = Utilities.getNameFromUrl(request.mUrl);
                    extension = Utilities.getExtensionFromMimeType(response.header("Content-Type"));
                    if (extension == null) {
                        extension = MimeTypeMap.getFileExtensionFromUrl(request.mUrl);
                    }
                }

                String filename;
                if (listener != null) {
                    filename = listener.onFixname(name, extension, request.mFilename);
                } else {
                    filename = request.mFilename;
                }
                request.mFilename = filename;

                // Use Temp filename
                uniFile = request.mDir.createFile(FileUtils.sanitizeFilename(filename + ".download"));
                if (uniFile == null) {
                    // Listener
                    if (listener != null) {
                        listener.onFailed(new IOException("Can't create file " + filename));
                    }
                    return false;
                }
                osPipe = new UniFileOutputStreamPipe(uniFile);
            }
            osPipe.obtain();

            long contentLength = body.contentLength();

            // Listener
            if (listener != null) {
                listener.onConnect(contentLength);
            }

            long receivedSize = transferData(body.byteStream(), osPipe.open(), listener);

            if (contentLength > 0 && contentLength != receivedSize) {
                throw new IOException("contentLength is " + contentLength + ", but receivedSize is " + receivedSize);
            }

            // Rename
            if (uniFile != null && request.mFilename != null) {
                uniFile.renameTo(request.mFilename);
            }

            // Listener
            if (listener != null) {
                listener.onSucceed();
            }
            return true;
        } catch (Exception e) {
            // remove download failed file
            if (uniFile != null) {
                uniFile.delete();
            }

            if (listener != null) {
                listener.onFailed(e);
            }
            return false;
        } finally {
            if (osPipe != null) {
                osPipe.close();
                osPipe.release();
            }
        }
    }

    public static class SimpleDownloadListener implements OnDownloadListener {

        @Override
        public void onStartDownloading() {
        }

        @Override
        public String onFixname(String newFilename, String newExtension, String oldFilename) {
            return oldFilename;
        }

        @Override
        public void onConnect(long totalSize) {
        }

        @Override
        public void onDonwlad(long receivedSize, long singleReceivedSize) {
        }

        @Override
        public void onFailed(Exception e) {
        }

        @Override
        public void onSucceed() {
        }
    }

    public interface OnDownloadListener {

        void onStartDownloading();

        String onFixname(String fileFirstname, String extension, String oldFilename);

        void onConnect(long totalSize);

        void onDonwlad(long receivedSize, long singleReceivedSize);

        void onFailed(Exception e);

        void onSucceed();
    }
}