/*
 * Syncany, www.syncany.org
 * Copyright (C) 2011-2016 Philipp C. Heckel <philipp.heckel@gmail.com>
 *
 * 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 org.syncany.config.to;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.output.ByteArrayOutputStream;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Commit;
import org.simpleframework.xml.core.Complete;
import org.simpleframework.xml.core.Persist;
import org.simpleframework.xml.core.Persister;
import org.syncany.config.ConfigException;
import org.syncany.crypto.CipherSpec;
import org.syncany.crypto.CipherUtil;
import org.syncany.crypto.SaltedSecretKey;
import org.syncany.util.StringUtil;

/**
 * The repo transfer object is used to create and load the repo file
 * from/to XML. The repo file identifies the repository with a unique
 * repo ID, and defines the chunking framework settings. It is
 * stored locally and on the remote storage.
 *
 * <p>It uses the Simple framework for XML serialization, and its corresponding
 * annotation-based configuration.
 *
 * @see <a href="http://simple.sourceforge.net/">Simple framework</a>
 * @author Philipp C. Heckel <philipp.heckel@gmail.com>
 */
@Root(name = "repo", strict = false)
public class RepoTO {
	@Element(name = "repoid", required = true)
	private String repoIdEncoded;
	private byte[] repoId;

	@Element(name = "chunker", required = false)
	private ChunkerTO chunker;

	@Element(name = "multichunker", required = false)
	private MultiChunkerTO multiChunker;

	@ElementList(name = "transformers", required = false, entry = "transformer")
	private ArrayList<TransformerTO> transformers;

	public byte[] getRepoId() {
		return repoId;
	}

	public void setRepoId(byte[] repoId) {
		this.repoId = repoId;
	}

	public void save(File file) throws ConfigException {
		try {
			new Persister().write(this, file);
		}
		catch (Exception e) {
			throw new ConfigException("Cannot write repoTO to file " + file, e);
		}
	}

	public void save(File file, List<CipherSpec> cipherSpecs, SaltedSecretKey masterKey) throws ConfigException {
		try {

			ByteArrayOutputStream plaintextRepoOutputStream = new ByteArrayOutputStream();

			Serializer serializer = new Persister();
			serializer.write(this, plaintextRepoOutputStream);

			CipherUtil.encrypt(new ByteArrayInputStream(plaintextRepoOutputStream.toByteArray()), new FileOutputStream(file), cipherSpecs, masterKey);
		}
		catch (Exception e) {
			throw new ConfigException("Cannot write repoTO (encrypted) to file " + file, e);
		}
	}

	@Persist
	public void prepare() {
		repoIdEncoded = (repoId != null) ? StringUtil.toHex(repoId) : null;
	}

	@Complete
	public void release() {
		repoIdEncoded = null;
	}

	@Commit
	public void commit() {
		repoId = (repoIdEncoded != null) ? StringUtil.fromHex(repoIdEncoded) : null;
	}

	public ChunkerTO getChunkerTO() {
		return chunker;
	}

	public void setChunkerTO(ChunkerTO chunker) {
		this.chunker = chunker;
	}

	public MultiChunkerTO getMultiChunker() {
		return multiChunker;
	}

	public void setMultiChunker(MultiChunkerTO multiChunker) {
		this.multiChunker = multiChunker;
	}

	public List<TransformerTO> getTransformers() {
		return transformers;
	}

	public void setTransformers(List<TransformerTO> transformers) {
		this.transformers = (transformers != null) ? new ArrayList<TransformerTO>(transformers) : null;
	}

	/**
	 * Configuration object for the deduplication chunker. As of
	 * today, this is a key/value based configuration.
	 */
	public static class ChunkerTO extends TypedPropertyListTO {
		// Nothing special about this
	}

	/**
	 * Configuration object for the deduplication multi-chunker. As of
	 * today, this is a key/value based configuration.
	 */
	public static class MultiChunkerTO extends TypedPropertyListTO {
		// Nothing special about this
	}

	/**
	 * Configuration object for the deduplication transformer. As of
	 * today, this is a key/value based configuration.
	 */
	public static class TransformerTO extends TypedPropertyListTO {
		// Nothing special about this
	}
}
