/*
 * SicMu Player - Lightweight music player for Android
 * Copyright (C) 2015  Mathieu Souchaud
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package souch.smp;

import android.content.Context;
import android.content.Intent;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;


import android.content.ContentUris;
import android.database.Cursor;
import android.provider.MediaStore;

import java.util.ArrayList;
import java.io.File;
import java.util.Collection;
import java.util.Formatter;
import java.util.HashSet;

public class Path {
    public final static char separatorChar = System.getProperty("file.separator", "/").charAt(0);
    public static String rootFolders = "";

    /*
     @param path must not be null
     rootFolders is removed from the beginning of the path
     if rootFolders = "" and path = /mnt/sdcard/toto/tata.mp3 -> return /mnt/sdcard/toto
     if rootFolders = "/mnt/sdcard" and path = /mnt/sdcard/toto/tata.mp3 -> return toto
     if rootFolders = "/mnt/sdcard/" and path = /mnt/sdcard/toto/tata.mp3 -> return toto
    */
    static public String getFolder(String path) {
        String folder = null;

        // remove rootFolders
        if (rootFolders != null) {
            String[] rootFoldersArray = rootFolders.split(";");
            for (String rootFolder : rootFoldersArray) {
                if (rootFolder.length() <= path.length() &&
                        rootFolder.equals(path.substring(0, rootFolder.length()))) {
                    int rootFolderSize = rootFolder.length();
                    // remove / remaining at the beginning of path
                    if (path.length() > rootFolderSize && path.charAt(rootFolderSize) == separatorChar)
                        rootFolderSize++;

                    folder = path.substring(rootFolderSize, path.length());
                }
            }
        }
        if (folder == null) {
            folder = path;
        }

        // remove filename
        int index = folder.lastIndexOf(separatorChar);
        if (index == -1) // no folder: remove everything
            index = 0;
        folder = folder.substring(0, index);

        // no folder get the name "."
        if (folder.equals(""))
            folder = ".";

        return folder;
    }

    /*
     path = "toto/tata" -> return {"toto", "tata"}
     */
    static public ArrayList<String> tokenizeFolder(String path) {
        ArrayList<String> folders = new ArrayList<>();
        int beg = 0;
        boolean folderFound = false;
        for(int i = 0; i < path.length(); i++) {
            if(path.charAt(i) == separatorChar) {
                if (folderFound) {
                    folders.add(path.substring(beg, i));
                    folderFound = false;
                }
                beg = i + 1;
            }
            else {
                folderFound = true;
            }
        }
        // path do not finish by /
        if (folderFound) {
            folders.add(path.substring(beg, path.length()));
        }

        return folders;
    }

    /*
     up "toto/tata" down "toto"  -> tata
     up "toto/tata" down ""      -> toto/tata
     up "toto/tata" toto/down "" -> ''
     */
    static public String cutFolder(String up, String down) {
        int i = 0;
        while(i < down.length() && i < up.length()  &&  up.charAt(i) == down.charAt(i))
            i++;
        return up.substring(i, up.length());
    }


    /**
     * From Android String.java
     *
     * modify compareToIgnoreCase in order to put shorter group to the end e.g.
     * normal compareToIgnoreCase order
     * /toto
     * /toto/tata
     * /toto/titi
     *
     * modified order (here)
     * /toto/tata
     * /toto/titi
     * /toto
     *
     * Compares this string to the given string, ignoring case differences.
     *
     * The drawback of this method being outside of String.java is that it is slower as it does not
     * play with internal string data (especially charAt calls). Rows initialization lose 15% of speed.
     */
    public static int compareToIgnoreCaseShorterFolderLast(String string1, String string2) {
        int o1 = 0, o2 = 0, result;
        int end = (string1.length() < string2.length() ? string1.length() : string2.length());
        char c1, c2;
        while (o1 < end) {
            if ((c1 = string1.charAt(o1++)) == (c2 = string2.charAt(o2++))) {
                continue;
            }
            c1 = foldCase(c1);
            c2 = foldCase(c2);
            if ((result = c1 - c2) != 0) {
                return result;
            }
        }
        return string2.length() - string1.length(); // modified here
    }
    /**
     * useful for compareToIgnoreCaseShorterFolderLast
     */
    private static char foldCase(char ch) {
        if (ch < 128) {
            if ('A' <= ch && ch <= 'Z') {
                return (char) (ch + ('a' - 'A'));
            }
            return ch;
        }
        return Character.toLowerCase(Character.toUpperCase(ch));
    }


    public static String getMusicStoragesStr(Context context) {
        Collection<File> dirs = getMusicStorages(context);
        String dirsStr = new String();
        for (File dir: dirs) {
            dirsStr += dir.getAbsolutePath() + ";";
        }
        if (dirsStr.endsWith(";"))
            dirsStr = dirsStr.substring(0, dirsStr.length() - 1);
        return dirsStr;
    }

    public static Collection<File> getMusicStorages(Context context) {

        Collection<File> dirs = getStorages(context);
        ArrayList<File> musicDirs = new ArrayList<>();
        for (File dir: dirs) {
            musicDirs.add(new File(dir, "Music/"));
        }
        return musicDirs;
    }

    public static Collection<File> getStorages(Context context) {
        HashSet<File> dirsToScan = new HashSet<>();

        dirsToScan.add(Environment.getExternalStorageDirectory());

        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            // hack. Don't know if it work well on other devices!
            String userPathToRemove = "Android/data/souch.smp/files";
            for (File dir : context.getExternalFilesDirs(null)) {
                if (dir.getAbsolutePath().endsWith(userPathToRemove)) {
                    dirsToScan.add(dir.getParentFile().getParentFile().getParentFile().getParentFile());
                }
            }
        }

        for (File dir: dirsToScan) {
            Log.d("Settings", "userDir: " + dir.getAbsolutePath());
        }
        return dirsToScan;
    }

    public static void listFiles(File directory, ArrayList<File> files) {
        // get all the files from a directory
        File[] fList = directory.listFiles();
        if (fList != null)
            for (File file : fList) {
                if (file.isFile()) {
                    files.add(file);
                } else if (file.isDirectory()) {
                    listFiles(file, files);
                }
            }
    }


    public static void rescanWhole(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            purgeFiles(context);
            scanMediaFiles(context);
        }
        else {
            if (android.os.Environment.getExternalStorageState().equals(
                    android.os.Environment.MEDIA_MOUNTED))
                // Broadcast the Media Scanner Intent to trigger it
                context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
                        Uri.parse("file://" + Environment.getExternalStorageDirectory())));

            Toast.makeText(context,
                    context.getResources().getString(R.string.settings_rescan_triggered), Toast.LENGTH_SHORT).show();
        }
    }


    public static boolean rescanDir(Context context, File dir) {
        if (!dir.exists())
            return false;
        Log.d("Settings", "fileToScan: " + dir.getAbsolutePath());
        ArrayList<File> filesToScan = new ArrayList<>();
        Path.listFiles(dir, filesToScan);
        scanMediaFiles(context, filesToScan);
        return true;
    }


    public static void purgeFiles(Context context)
    {
        final String [] projection = {MediaStore.Audio.Media._ID, MediaStore.Audio.Media.DATA};
        Uri playlist_uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
        Cursor cursor = context.getContentResolver().query(playlist_uri, projection, null,null,null);
        cursor.moveToFirst();
        for (int r = 0; r < cursor.getCount(); r++, cursor.moveToNext()){
            int id = cursor.getInt(0);
            String filePath = cursor.getString(1);
            Boolean delIt = true;
            if (filePath.length() > 0) {
                File file = new File(filePath);
                if (file.exists())
                    delIt = false;
            }
            if (delIt) {
                // TODO: confirm it (yes, yes to all, no, no to all) rather than toast it
                Toast.makeText(context,
                        (new Formatter()).format(context.getResources()
                                .getString(R.string.settings_rescan_purge), filePath)
                                .toString(),
                        Toast.LENGTH_SHORT).show();
                Uri uri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);
                context.getContentResolver().delete(uri, null, null);
            }
        }
        cursor.close();
    }

    private static void scanMediaFiles(Context context) {
        // http://stackoverflow.com/questions/13270789/how-to-run-media-scanner-in-android
        Toast.makeText(context,
                context.getString(R.string.settings_rescan_triggered),
                Toast.LENGTH_SHORT).show();

        Collection<File> dirsToScan = Path.getStorages(context); // getBaseContext()

        for (File dir: dirsToScan) {
            Toast.makeText(context,
                    (new Formatter()).format(context.getResources()
                            .getString(R.string.settings_rescan_storage), dir)
                            .toString(),
                    Toast.LENGTH_LONG).show();
        }

        // add Music folder in first to speedup music folder discovery
        for (File dir: dirsToScan) {
            File musicDir = new File(dir, "Music");
            rescanDir(context, musicDir);
        }

        // add whole storage at the end
        for (File dir: dirsToScan) {
            rescanDir(context, dir);
        }

        Toast.makeText(context,
                context.getResources().getString(R.string.settings_rescan_finished),
                Toast.LENGTH_LONG).show();
    }


    private static void scanMediaFiles(Context context, Collection<File> filesToScan) {
        String[] filesToScanArray = new String[filesToScan.size()];
        int i = 0;
        for (File file : filesToScan) {
            filesToScanArray[i] = file.getAbsolutePath();
            //if (filesToScanArray[i].contains("emulated/0"))
                Log.d("Settings", "fileToScan: " + filesToScanArray[i]);
            i++;
        }

        if (filesToScanArray.length != 0) {
            MediaScannerConnection.scanFile(context, filesToScanArray, null, null);
        } else {
            Log.e("Settings", "Media scan requested when nothing to scan");
        }
    }



    static private final String[] imageFileExtensions =  new String[] {"jpg", "png", "gif", "jpeg"};
    static public boolean isImage(File file) {
        if (file.isFile())
            for (String extension : imageFileExtensions)
                if (file.getName().toLowerCase().endsWith(extension))
                    return true;
        return false;
    }

}