package usda.weru.gis.gui;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.time.Instant;
import java.util.ArrayList;
import org.apache.commons.io.FileUtils;
import usda.weru.util.About;
import usda.weru.util.ConfigData;

/**
 *
 * @author mark
 */
public class TileCacheCleaner {
    
    private final File cacheDir;
    
    public TileCacheCleaner () {
        cacheDir = getCacheDir();
    }
    
    public File getCacheDir () {
        return new File(About.getWepsCacheDirTiles().getAbsolutePath());
    }
    
    static public void checkAndCleanTileCacheInThread () {
        new Thread () {
            public void run() {
                TileCacheCleaner cleaner = new TileCacheCleaner ();
                
                TileCacheCleaner.SummaryResult res = cleaner.doGetCacheSummary();
                
                // for now, check if parm is in ConfigData yet.
                // Once it gets into the default Weps configdata, then this can be removed
                String s = ConfigData.getDefault().getData(ConfigData.layerCacheSizeLimit);
                if (s == null) {
                    ConfigData.getDefault().setData(ConfigData.layerCacheSizeLimit,"256");
                }
                
                int size = ConfigData.getIntParm(ConfigData.layerCacheSizeLimit, 256);
                size *= 1000000;
                while (res.totalSize > size) {
                    cleaner.doClearOldest();

                    res = cleaner.doGetCacheSummary();
                }
            }
        }.start();
        
    }
    
    public SummaryResult doGetCacheSummary () {
        SummaryVisitor<Path> visitor = new SummaryVisitor<>();
        try {
            Files.walkFileTree(getCacheDir().toPath(), visitor);
        } catch (IOException ex) {
            int j = 1;
        }
        return visitor.result;
    }
    
    public OldestResult doGetOldest () {
        FindOldestVisitor<Path> visitor = new FindOldestVisitor<>();
        try {
            Files.walkFileTree(getCacheDir().toPath(), visitor);
        } catch (IOException ex) {
            int j = 1;
        }
        return visitor.result;
    }
    
    public void doClearOldest () {
        OldestResult res = doGetOldest ();
        
        for (int i=0; i<res.count; i++) {
            try {
                Files.delete(res.files.get(i));
            } catch (IOException ex) {
                int j = 1;
            }
        }
    }
    
    public class ClearVisitor<P> extends SimpleFileVisitor<P> {
            
        public ClearVisitor () {
        }
        
        @Override
        public FileVisitResult preVisitDirectory(P dir, BasicFileAttributes attrs)
            throws IOException {
            File f = ((Path)dir).toFile();
            FileUtils.deleteDirectory(f);
            f.mkdirs();
            return FileVisitResult.SKIP_SUBTREE;
        }
    }
        
    public class SummaryResult {
        public long totalSize;
        public FileTime earliestTime;
        
        public SummaryResult () {
            totalSize = 0;
            earliestTime = FileTime.from(Instant.now());
        }
    }
    
    public class SummaryVisitor<P> extends SimpleFileVisitor<P> {
        
        public SummaryResult result;

        public SummaryVisitor () {
            result = new SummaryResult ();
        }

        @Override
        public FileVisitResult visitFile(P file, BasicFileAttributes attrs) {
            result.totalSize += attrs.size();
            if (attrs.lastAccessTime().compareTo(result.earliestTime) < 0) {
                result.earliestTime = attrs.lastAccessTime();
            }

            return FileVisitResult.CONTINUE;
        }
    }
    
        
    public class OldestResult {
        public final int count = 256;
        public ArrayList<Path> files;
        public ArrayList<FileTime> times;
        public ArrayList<String> names;
        
        public OldestResult () {
            files = new ArrayList<>();
            times = new ArrayList<>();
            names = new ArrayList<>();
        }
        
        public void checkAndAdd (Path file, BasicFileAttributes attrs) {
            FileTime fileTime = attrs.lastAccessTime();
            if (files.size() < count) {
                add (file, fileTime);
            } else {
                for (int i=0; i<count; i++) {
                    FileTime t = times.get(i);
                    if (fileTime.compareTo(t) < 0) {
                        set (file, fileTime, i);
                        break;
                    }
                }
            }
        }
        
        private void add (Path file, FileTime fileTime) {
            files.add(file);
            times.add(fileTime);
            names.add(file.toString());
        }
        private void set (Path file, FileTime fileTime, int i) {
            files.set(i,file);
            times.set(i,fileTime);
            names.set(i, file.toString());
        }
        
        private void replace (Path file, FileTime fileTime, int i) {
            
        }
    }
    
    public class FindOldestVisitor<P> extends SimpleFileVisitor<P> {
        
        public OldestResult result ;

        public FindOldestVisitor () {
            result = new OldestResult ();
        }

        @Override
        public FileVisitResult visitFile(P file, BasicFileAttributes attrs) {
            result.checkAndAdd((Path)file, attrs);

            return FileVisitResult.CONTINUE;
        }
    }
}
