package org.jivesoftware.smackx.ikey;

import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.xml.SmackXmlParser;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.ikey.element.IkeyElement;
import org.jivesoftware.smackx.ikey.provider.IkeyElementProvider;
import org.jxmpp.jid.EntityBareJid;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;

public class FileBasedIkeyStore implements IkeyStore {

    private final File baseFile;

    public FileBasedIkeyStore(File file) {
        this.baseFile = Objects.requireNonNull(file);
    }

    @Override
    public IkeyElement loadIkeyRecord(EntityBareJid jid) throws IOException {
        File file = new File(baseFile, jid.asUrlEncodedString());
        if (file.exists() && !file.isDirectory()) {
            return null;
        }
        try {
            String content = getFileContent(new FileInputStream(file));
            return IkeyElementProvider.INSTANCE.parse(getParser(content));
        } catch (XmlPullParserException | SmackParsingException e) {
            throw new IOException(e);
        }
    }

    @Override
    public void storeIkeyRecord(EntityBareJid jid, IkeyElement record) throws IOException {
        File file = new File(baseFile, jid.asUrlEncodedString());
        if (!file.exists()) {
            file.createNewFile();
        }
        writeToFile(new FileOutputStream(file), record.toXML().toString());
    }

    private static String getFileContent(FileInputStream fis) throws IOException {
        try (BufferedReader br = new BufferedReader(new InputStreamReader(fis, StandardCharsets.UTF_8))) {
            StringBuilder sb = new StringBuilder();
            String line;
            while((line = br.readLine()) != null) {
                sb.append(line);
                sb.append('\n');
            }
            return sb.toString();
        }
    }

    private static void writeToFile(FileOutputStream fos, String content) throws IOException {
        try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos, StandardCharsets.UTF_8))) {
            bw.write(content);
        }
    }

    private static XmlPullParser getParser(String xml) {
        return getParser(new StringReader(xml), null);
    }

    private static XmlPullParser getParser(Reader reader, String startTag) {
        XmlPullParser parser;
        try {
            parser = SmackXmlParser.newXmlParser(reader);
            if (startTag == null) {
                while (parser.getEventType() != XmlPullParser.Event.START_ELEMENT) {
                    parser.next();
                }
                return parser;
            }
            boolean found = false;

            while (!found) {
                if ((parser.next() == XmlPullParser.Event.START_ELEMENT) && parser.getName().equals(startTag))
                    found = true;
            }

            if (!found)
                throw new IllegalArgumentException("Can not find start tag '" + startTag + "'");
        } catch (XmlPullParserException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return parser;
    }


}
