/*
 * Copyright 2014 Paul Tsouchlos
 *
 *    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.github.developerpaul123.filepickerlibrary.adapter;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.AsyncTask;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.afollestad.materialdialogs.MaterialDialog;
import com.github.developerpaul123.filepickerlibrary.R;
import com.github.developerpaul123.filepickerlibrary.enums.Scope;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.lang.ref.WeakReference;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;

/**
 * Created by Paul Tsouchlos
 */
public class FileListAdapter extends BaseAdapter {

    private final Context mContext;
    private final List<File> fileList;
    private final LayoutInflater inflater;
    private int selectedPos;
    private Bitmap bitmap;
    private final float iconPadding;
    private final Drawable folderDrawable;

    private final Scope mFileType;

    public FileListAdapter(Context context, File[] fileArray, Scope type) {
        mContext = context;
        fileList = new ArrayList<File>(Arrays.asList(fileArray));
        inflater = LayoutInflater.from(mContext);
        mFileType = type;
        selectedPos = -1;
        folderDrawable = mContext.getResources().getDrawable(R.drawable.fplib_ic_folder);
        iconPadding = mContext.getResources().getDimension(R.dimen.file_picker_lib_default_icon_padding);
        if (mFileType == Scope.DIRECTORIES) {
            for (int i = 0; i < fileList.size(); i++) {
                String extension = fileExt(fileList.get(i).getPath());
                if (extension != null) {
                    fileList.remove(i);
                }
            }
        }
    }

    /**
     * Returns the file extension of a file.
     *
     * @param url the file path
     * @return
     */
    private String fileExt(String url) {
        if (url.indexOf("?") > -1) {
            url = url.substring(0, url.indexOf("?"));
        }
        if (url.lastIndexOf(".") == -1) {
            return null;
        } else {
            String ext = url.substring(url.lastIndexOf("."));
            if (ext.indexOf("%") > -1) {
                ext = ext.substring(0, ext.indexOf("%"));
            }
            if (ext.indexOf("/") > -1) {
                ext = ext.substring(0, ext.indexOf("/"));
            }
            return ext.toLowerCase();

        }
    }

    /**
     * From the google examples, decodes a bitmap as a byte array and then resizes it for the required
     * width and hieght.
     *
     * @param picture   the picture byte array
     * @param reqWidth  the required width
     * @param reqHeight the required height
     * @return a Bitmap
     */
    public static Bitmap decodeSampledBitmapFromByteArray(byte[] picture,
                                                          int reqWidth, int reqHeight) {

        // First decode with inJustDecodeBounds=true to check dimensions
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeByteArray(picture, 0, picture.length, options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeByteArray(picture, 0, picture.length, options);
    }

    public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        int height = options.outHeight;
        int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            // Calculate ratios of height and width to requested height and width
            int heightRatio = Math.round((float) height / (float) reqHeight);
            int widthRatio = Math.round((float) width / (float) reqWidth);

            // Choose the smallest ratio as inSampleSize value, this will guarantee
            // a final image with both dimensions larger than or equal to the
            // requested height and width.
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }

        return inSampleSize;
    }

    /**
     * Encodes a bitmap to a byte array.
     *
     * @param bitmap the bitmap to compress
     * @param format the compression format for the Bitmap
     * @return {@code byte[]} object
     */
    public static byte[] encodeBitmapToArray(Bitmap bitmap, Bitmap.CompressFormat format) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        bitmap.compress(format, 0, outputStream);
        return outputStream.toByteArray();

    }

    @Override
    public int getCount() {
        return fileList.size();
    }

    @Override
    public Object getItem(int i) {
        return fileList.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        FileListAdapter.ViewHolder viewHolder;
        final int position = i;
        if (view == null) {
            view = inflater.inflate(R.layout.file_list_item, null);
            viewHolder = new FileListAdapter.ViewHolder();
            viewHolder.fileInfo = (TextView) view.findViewById(R.id.file_item_file_info);
            viewHolder.fileTitle = (TextView) view.findViewById(R.id.file_item_file_name);
            viewHolder.fileImage = (ImageView) view.findViewById(R.id.file_item_image_view);
            viewHolder.fileInfoButton = (ImageView) view.findViewById(R.id.file_item_file_info_button);
            view.setTag(viewHolder);
        } else {
            viewHolder = (FileListAdapter.ViewHolder) view.getTag();
        }

        if (selectedPos == i) {
            view.setBackgroundColor(mContext.getResources()
                    .getColor(R.color.card_detailing));
        } else {
            view.setBackgroundColor(mContext.getResources()
                    .getColor(android.R.color.background_light));
        }

        viewHolder.fileInfoButton.setBackgroundDrawable(getFileDrawable(R.drawable.fplib_ic_action_info));
        viewHolder.fileInfoButton.setClickable(true);
        viewHolder.fileInfoButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                View customView = LayoutInflater.from(v.getContext()).inflate(R.layout.file_info_layout, null);
                TextView fileSize = (TextView) customView.findViewById(R.id.file_info_size);
                TextView fileDate = (TextView) customView.findViewById(R.id.file_info_date_created);
                TextView filePath = (TextView) customView.findViewById(R.id.file_info_path);
                File file = fileList.get(position);
                if (!file.isDirectory()) {
                    fileSize.setText(String.format(
                            mContext.getString(R.string.file_picker_adapter_size_string), file.length()));
                }
                Calendar cal = Calendar.getInstance();
                cal.setTimeInMillis(file.lastModified());
                DateFormat df = DateFormat.getDateInstance();
                fileDate.setText(String.format(mContext.getString(R.string.file_picker_adapter_last_modified_string), df.format(cal.getTime())));
                filePath.setText(String.format(mContext.getString(R.string.file_picker_adapter_file_path_string), file.getAbsolutePath()));
                new MaterialDialog.Builder(v.getContext())
                        .title(String.format(mContext.getString(R.string.file_picker_file_info_dialog_file_path), fileList.get(position).getName()))
                        .customView(customView, true)
                        .show();
            }
        });

        if (mFileType == Scope.ALL) {
            viewHolder.fileTitle.setText(fileList.get(i).getName());
            if (!fileList.get(i).isDirectory()) {
                viewHolder.fileInfo.setText(String.format(mContext.getString(R.string.file_picker_adapter_file_size_only_string), fileList.get(i).length()));
            }
            String fileExt = fileExt(fileList.get(i).toString());
            if (fileList.get(i).isDirectory()) {
                viewHolder.fileImage.setBackgroundDrawable(getFileDrawable(R.drawable.fplib_ic_action_file_folder));
            } else {
                if (fileExt != null) {
                    if (fileExt.equalsIgnoreCase(".doc")) {
                        viewHolder.fileImage.setBackgroundDrawable(getFileDrawable(R.drawable.fplib_ic_doc_file));
                    } else if (fileExt.equalsIgnoreCase(".docx")) {
                        viewHolder.fileImage.setBackgroundDrawable(getFileDrawable(R.drawable.fplib_ic_doc_file));
                    } else if (fileExt.equalsIgnoreCase(".xls")) {
                        viewHolder.fileImage.setBackgroundDrawable(getFileDrawable(R.drawable.fplib_ic_xls_file));
                    } else if (fileExt.equalsIgnoreCase(".xlsx")) {
                        viewHolder.fileImage.setBackgroundDrawable(getFileDrawable(R.drawable.fplib_ic_xlsx_file));
                    } else if (fileExt.equalsIgnoreCase(".xml")) {
                        viewHolder.fileImage.setBackgroundDrawable(getFileDrawable(R.drawable.fplib_ic_xml_file));
                    } else if (fileExt.equalsIgnoreCase(".html")) {
                        viewHolder.fileImage.setBackgroundDrawable(getFileDrawable(R.drawable.fplib_ic_html_file));
                    } else if (fileExt.equalsIgnoreCase(".pdf")) {
                        viewHolder.fileImage.setBackgroundDrawable(getFileDrawable(R.drawable.fplib_ic_pdf_file));
                    } else if (fileExt.equalsIgnoreCase(".txt")) {
                        viewHolder.fileImage.setBackgroundDrawable(getFileDrawable(R.drawable.fplib_ic_txt_file));
                    } else if (fileExt.equalsIgnoreCase(".jpeg")) {
                        viewHolder.fileImage.setBackgroundDrawable(mContext.getResources()
                                .getDrawable(R.drawable.fplib_rectangle));
                        new BitmapWorkerTask(viewHolder.fileImage, Bitmap.CompressFormat.JPEG).execute(fileList.get(i));
                    } else if (fileExt.equalsIgnoreCase(".jpg")) {
                        viewHolder.fileImage.setBackgroundDrawable(mContext.getResources()
                                .getDrawable(R.drawable.fplib_rectangle));
                        new BitmapWorkerTask(viewHolder.fileImage, Bitmap.CompressFormat.JPEG).execute(fileList.get(i));
                    } else if (fileExt.equalsIgnoreCase(".png")) {
                        viewHolder.fileImage.setBackgroundDrawable(mContext.getResources()
                                .getDrawable(R.drawable.fplib_rectangle));
                        new BitmapWorkerTask(viewHolder.fileImage, Bitmap.CompressFormat.PNG).execute(fileList.get(i));
                    } else {
                        viewHolder.fileImage.setBackgroundDrawable(mContext.getResources()
                                .getDrawable(R.drawable.fplib_ic_default_file));
                    }
                }
            }
        } else if (mFileType == Scope.DIRECTORIES) {
            if (fileList.get(i).isDirectory()) {
                viewHolder.fileImage.setBackgroundDrawable(folderDrawable);
                viewHolder.fileTitle.setText(fileList.get(i).getName());
            }
        }
        return view;
    }

    private Drawable getFileDrawable(int fileResource) {
        Drawable firstLayer = mContext.getResources().getDrawable(fileResource);
        LayerDrawable drawable = new LayerDrawable(new Drawable[]{
                mContext.getResources().getDrawable(R.drawable.fplib_circle),
                firstLayer
        });

        drawable.setLayerInset(1, (int) iconPadding, (int) iconPadding,
                (int) iconPadding, (int) iconPadding);

        return drawable;
    }

    public int getSelectedPosition() {
        return selectedPos;
    }

    public void setSelectedPosition(int pos) {
        selectedPos = pos;
        // inform the view of this change
        notifyDataSetChanged();
    }

    private static class ViewHolder {
        TextView fileTitle;
        TextView fileInfo;
        ImageView fileImage;
        ImageView fileInfoButton;
    }

    /**
     * Class that handles the loading of a bitmap.
     */
    private class BitmapWorkerTask extends AsyncTask<File, Void, Bitmap> {
        private final WeakReference<ImageView> imageViewReference;
        private byte[] data;
        private final Bitmap.CompressFormat mFormat;

        public BitmapWorkerTask(ImageView imageView, Bitmap.CompressFormat format) {
            // Use a WeakReference to ensure the ImageView can be garbage collected
            imageView.setBackgroundDrawable(imageView.getContext()
                    .getResources().getDrawable(R.drawable.fplib_rectangle));
            imageViewReference = new WeakReference<ImageView>(imageView);
            mFormat = format;
        }

        // Decode image in background.
        @Override
        protected Bitmap doInBackground(File... params) {
            Bitmap mBitmap = BitmapFactory.decodeFile(params[0].getAbsolutePath());
            //check if bitmap is null here.
            if (mBitmap != null) {
                data = encodeBitmapToArray(mBitmap, mFormat);
                return decodeSampledBitmapFromByteArray(data, 54, 54);
            } else {
                return null;
            }
        }

        // Once complete, see if ImageView is still around and set bitmap.
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if (imageViewReference.get() != null && bitmap != null) {
                ImageView imageView = imageViewReference.get();
                if (imageView != null) {
                    imageView.setImageBitmap(bitmap);
                    imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
                }
            }
        }
    }

    private class GetFileSizeTask extends AsyncTask<Void, Void, Long> {

        private final WeakReference<TextView> textViewWeakReference;
        private final File file;
        private final String formatString;

        private GetFileSizeTask(TextView textView, File f, String string) {
            file = f;
            textViewWeakReference = new WeakReference<TextView>(textView);
            formatString = string;
        }

        private long getDirectorySize(File directory) {
            File[] files = directory.listFiles();
            int size = 0;
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        size += getDirectorySize(file);
                    } else {
                        size += file.length();
                    }
                }
            }
            return size;
        }        @Override
        protected Long doInBackground(Void... params) {
            return getDirectorySize(file);
        }

        @Override
        protected void onPostExecute(Long aLong) {
            TextView textView = textViewWeakReference.get();
            if (textView != null) {
                textView.setText(String.format(formatString, aLong));
            }
        }


    }

}