package photato;

import java.util.logging.Logger;
import java.util.logging.Level;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.nio.file.FileSystem;
import photato.core.PhotatoFilesManager;
import photato.core.metadata.MetadataAggregator;
import photato.core.resize.thumbnails.ThumbnailGenerator;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.util.concurrent.TimeUnit;
import org.apache.http.config.SocketConfig;
import org.apache.http.impl.bootstrap.HttpServer;
import org.apache.http.impl.bootstrap.ServerBootstrap;
import photato.core.resize.fullscreen.FullScreenImageGetter;
import photato.core.metadata.gps.IGpsCoordinatesDescriptionGetter;
import photato.controllers.CssHandler;
import photato.controllers.DefaultHandler;
import photato.controllers.FolderListHandler;
import photato.controllers.ImageHandler;
import photato.controllers.JsHandler;
import photato.core.metadata.exif.ExifToolDownloader;
import photato.core.metadata.gps.OSMGpsCoordinatesDescriptionGetter;
import java.nio.file.Path;
import java.util.Enumeration;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import photato.controllers.LoadingHandler;
import photato.controllers.VideoHandler;
import photato.core.resize.ffmpeg.FfmpegDownloader;

public class Photato {
    private static Logger LOGGER;

    public static final String[] supportedPictureExtensions = new String[]{"jpg", "jpeg", "png", "bmp"};
    public static final String[] supportedVideoExtensions = new String[]{"mp4", "webm"};
    private static final String serverName = "Photato";

    public static void main(String[] args) throws Exception {
        //Set up this way so we can change default formatter for everyone
        System.setProperty("java.util.logging.SimpleFormatter.format", 
            "%1$tF %1$tT %4$s %2$s %5$s%6$s%n");

        LOGGER = Logger.getLogger( Photato.class.getName() );
        
        if (args.length < 1) {
            LOGGER.log( Level.SEVERE, "Usage: <picturesRootFolder> [cacheFolder] [configFolder]");
            System.exit(-1);
        }

        FileSystem fileSystem = FileSystems.getDefault();
        Path rootFolder = getRootFolder(fileSystem, args[0]);
        String cacheRootFolder = (args.length >= 2 ? args[1] : "cache");
        String thumbnailCacheFolder = cacheRootFolder + "/thumbnails";
        String fullscreenCacheFolder = cacheRootFolder + "/fullscreen";
        String metadataCacheLocation = cacheRootFolder + "/metadata.cache";
        String extractedPicturesCacheFolder = cacheRootFolder + "/extracted";
        String configFile = (args.length >= 3 ? args[2] : ".") + "/photato.ini";

        PhotatoConfig.init(configFile);

        LOGGER.log(Level.INFO, "Starting photato");
        LOGGER.log(Level.INFO, "-- Config file: {0}", configFile);
        LOGGER.log(Level.INFO, "-- Cache file: {0}", cacheRootFolder);
        LOGGER.log(Level.INFO, "-- Pictures file: {0}", rootFolder);
        
        HttpServer server = getDefaultServer(fileSystem.getPath("www"));
        server.start();

        if (!Files.exists(fileSystem.getPath(cacheRootFolder))) {
            LOGGER.log(Level.INFO, "Creating cache folder");
            Files.createDirectory(fileSystem.getPath(cacheRootFolder));
        }

        HttpClient httpClient = HttpClientBuilder.create().setUserAgent(serverName).build();

        ExifToolDownloader.run(httpClient, fileSystem, PhotatoConfig.forceFfmpegToolsDownload);
        FfmpegDownloader.run(httpClient, fileSystem, PhotatoConfig.forceExifToolsDownload);

        ThumbnailGenerator thumbnailGenerator = new ThumbnailGenerator(fileSystem, rootFolder, thumbnailCacheFolder, extractedPicturesCacheFolder, PhotatoConfig.thumbnailHeight, PhotatoConfig.thumbnailQuality);
        IGpsCoordinatesDescriptionGetter gpsCoordinatesDescriptionGetter = new OSMGpsCoordinatesDescriptionGetter(httpClient, PhotatoConfig.addressElementsCount);
        MetadataAggregator metadataGetter = new MetadataAggregator(fileSystem, metadataCacheLocation, gpsCoordinatesDescriptionGetter);
        FullScreenImageGetter fullScreenImageGetter = new FullScreenImageGetter(fileSystem, rootFolder, fullscreenCacheFolder, extractedPicturesCacheFolder, PhotatoConfig.fullScreenPictureQuality, PhotatoConfig.maxFullScreenPictureWitdh, PhotatoConfig.maxFullScreenPictureHeight);

        PhotatoFilesManager photatoFilesManager = new PhotatoFilesManager(rootFolder, fileSystem, metadataGetter, thumbnailGenerator, fullScreenImageGetter, PhotatoConfig.prefixModeOnly, PhotatoConfig.indexFolderName, PhotatoConfig.useParallelPicturesGeneration);

        // Closing tmp server
        server.shutdown(5, TimeUnit.SECONDS);

        while (true) {
            try {
                server = ServerBootstrap.bootstrap()
                        .setListenerPort(PhotatoConfig.serverPort)
                        .setServerInfo(serverName)
                        .setSocketConfig(getSocketConfig())
                        .setExceptionLogger(new StdErrorExceptionLogger())
                        .registerHandler(Routes.rawVideosRootUrl + "/*", new VideoHandler(rootFolder, Routes.rawVideosRootUrl))
                        .registerHandler(Routes.rawPicturesRootUrl + "/*", new ImageHandler(rootFolder, Routes.rawPicturesRootUrl))
                        .registerHandler(Routes.fullScreenPicturesRootUrl + "/*", new ImageHandler(fileSystem.getPath(fullscreenCacheFolder), Routes.fullScreenPicturesRootUrl))
                        .registerHandler(Routes.thumbnailRootUrl + "/*", new ImageHandler(fileSystem.getPath(thumbnailCacheFolder), Routes.thumbnailRootUrl))
                        .registerHandler(Routes.listItemsApiUrl, new FolderListHandler(Routes.listItemsApiUrl, photatoFilesManager))
                        .registerHandler("/img/*", new ImageHandler(fileSystem.getPath("www/img"), "/img"))
                        .registerHandler("/js/*", new JsHandler(fileSystem.getPath("www/js"), "/js"))
                        .registerHandler("/css/*", new CssHandler(fileSystem.getPath("www/css"), "/css"))
                        .registerHandler("*", new DefaultHandler(fileSystem.getPath("www")))
                        .create();
                server.start();

                LOGGER.log(Level.INFO, "Server started http://{0}:{1}", new Object[] {getLocalIp(), server.getLocalPort()});
                server.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
            } catch (IOException | InterruptedException ex) {
                // In case of port already binded
                LOGGER.log( Level.SEVERE, "Could not start the server ...");
                Thread.sleep(1000);
            }
        }
    }

    private static Path getRootFolder(FileSystem fileSystem, String args0) {
        if (!args0.endsWith("/")) {
            args0 += "/";
        }
        return fileSystem.getPath(args0);
    }

    private static String getLocalIp() {
        try {
            InetAddress inet = InetAddress.getLocalHost();
            InetAddress[] ips = InetAddress.getAllByName(inet.getCanonicalHostName());
            for (InetAddress ip : ips) {
                String ipStr = ip.getHostAddress();
                if (ipStr.startsWith("192.168.")) {
                    return ipStr;
                }
            }
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static HttpServer getDefaultServer(Path folderRoot) {
        return ServerBootstrap.bootstrap()
                .setListenerPort(PhotatoConfig.serverPort)
                .setServerInfo(serverName)
                .setSocketConfig(getSocketConfig())
                .setExceptionLogger(new StdErrorExceptionLogger())
                .registerHandler("*", new LoadingHandler(folderRoot))
                .create();
    }

    private static SocketConfig getSocketConfig() {
        return SocketConfig.custom()
                .setSoTimeout(60000)
                .setTcpNoDelay(true)
                .build();
    }

}