package com.jtv.solr.handler.dataimport;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.nio.CharBuffer;
import org.apache.solr.handler.dataimport.FileDataSource;
import org.apache.solr.handler.dataimport.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Properties;

/**
 * 
 * File Data Source implementation that moves the file to an archive directory
 * when finished. 
 * 
 * @author Josh Harness
 * 
 * josh.harness@jtv.com
 */
public class ArchivingFileDataSource extends FileDataSource {

    public static final String ARCHIVE_PROPERTY_NAME = "archiveDir";
    private static final Logger LOG = LoggerFactory.getLogger(FileDataSource.class);
    private File archive;

    @Override
    public void init(Context context, Properties initProps) {
        super.init(context, initProps);

        String archive = initProps.getProperty(ARCHIVE_PROPERTY_NAME);
        if (archive != null) {
            archive = context.replaceTokens(archive);
            initializeArchiveDirectory(archive);
            LOG.info("Archive directory: " + archive);
        } else {
            LOG.warn("Archive directory not provided. Defaulting to non-archive implementation!");
        }
    }

    @Override
    protected Reader openStream(File file) throws FileNotFoundException,
                                                  UnsupportedEncodingException {

        Reader reader = super.openStream(file);

        // no archiving if the archive directory was not initialized
        if (archive == null) {
            return reader;
        }

        reader = new ArchivingWrappedReader(reader, file, archive);

        return reader;
    }

    private void initializeArchiveDirectory(String path) {

        File f = new File(path);
        if (!f.exists()) {
            throw new IllegalArgumentException("Path " + path + " does not exist!");
        }


        if (!f.isDirectory()) {
            throw new IllegalArgumentException("Path " + path + " is not a directory!");
        }

        if (!f.canWrite()) {
            throw new IllegalArgumentException("Path " + path + " is not writable!");
        }

        this.archive = f;
    }

    private class ArchivingWrappedReader extends Reader {

        private Reader reader;
        private File file;
        private File archive;

        ArchivingWrappedReader(Reader reader, File file, File archive) {
            this.reader = reader;
            this.file = file;
            this.archive = archive;
        }

        @Override
        public void close() throws IOException {

            reader.close();
            boolean success = file.renameTo(new File(archive, file.getName()));

            if (!success) {
                String msg = "Error renaming file " + file.getName() + " to archive directory";
                LOG.error(msg);
                throw new IOException(msg);
            }
        }

        /* The Following Methods Delegate to the Wrapped Reader */
        @Override
        public void mark(int i) throws IOException {
            reader.mark(i);
        }

        @Override
        public boolean markSupported() {
            return reader.markSupported();
        }

        @Override
        public int read(CharBuffer cb) throws IOException {
            return reader.read(cb);
        }

        @Override
        public int read() throws IOException {
            return reader.read();
        }

        @Override
        public int read(char[] chars) throws IOException {
            return reader.read(chars);
        }

        @Override
        public boolean ready() throws IOException {
            return reader.ready();
        }

        @Override
        public void reset() throws IOException {
            reader.reset();
        }

        @Override
        public long skip(long l) throws IOException {
            return reader.skip(l);
        }

        @Override
        public int read(char[] chars, int i, int i1) throws IOException {
            return reader.read(chars, i, i1);
        }
    }
}
