/*
 * 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.tests.integration.scenarios;

import static org.junit.Assert.assertEquals;

import java.io.File;
import java.io.FilenameFilter;

import org.junit.Test;
import org.syncany.operations.down.DownOperationResult;
import org.syncany.operations.up.UpOperationOptions;
import org.syncany.plugins.local.LocalTransferSettings;
import org.syncany.tests.util.TestClient;
import org.syncany.tests.util.TestConfigUtil;

public class Issue227_2_ScenarioTest {
	@Test
	public void testIssue227_MOM() throws Exception {
		// Setup
		LocalTransferSettings testConnection = (LocalTransferSettings) TestConfigUtil.createTestLocalConnection();

		TestClient clientIH = new TestClient("iH", testConnection);
		TestClient clientMOM = new TestClient("MOM", testConnection);
		TestClient clientHSE = new TestClient("hSE", testConnection);

		UpOperationOptions upOptionsWithForce = new UpOperationOptions();
		upOptionsWithForce.setForceUploadEnabled(true);

		clientIH.createNewFile("file1.jpg");
		clientIH.up();

		clientIH.createNewFile("file2.jpg");
		clientIH.up();

		clientMOM.down();
		clientHSE.down();

		// -

		clientMOM.createNewFile("file3.jpg");
		clientMOM.up();

		clientMOM.createNewFile("file4.jpg");
		clientMOM.up();

		clientIH.down();
		clientHSE.down();

		// All in sync

		/*
		 * We want to create a time difference situation here between different clients.
		 *
		 * In reality:
		 * - Client "hSE" uploads a new database AFTER client "MOM"
		 *
		 * In this test:
		 * 1. Client "hSE" uploads a new database BEFORE client "MOM"
		 * 2. We hide "hSE"'s database by moving it to a temp. file
		 *    // ...
		 * 5. When we do 'down' at client "IH", the databases of client "MOM" are considered DIRTY
		 *
		 */

		// 1. Upload new database for hSE
		clientHSE.createNewFile("fileHSE-1.jpg");
		clientHSE.up(upOptionsWithForce);

		File[] hSEDatabaseFiles = new File(testConnection.getPath() + "/databases/").listFiles(new FilenameFilter() {
			public boolean accept(File dir, String name) {
				return name.contains("hSE");
			}
		});

		assertEquals(1, hSEDatabaseFiles.length);

		// 2. Hide database from other clients
		File hSEDatabaseFile = hSEDatabaseFiles[0];
		File hSEDatabaseFileHidden = new File(hSEDatabaseFile.getParentFile(), "HIDE_THIS_FILE_" + hSEDatabaseFile.getName());

		hSEDatabaseFile.renameTo(hSEDatabaseFileHidden);

		// 3. This shouldn't do anything; no new databases!
		DownOperationResult downOperationResult = clientIH.down();
		assertEquals(0, downOperationResult.getDownloadedUnknownDatabases().size());

		// 4. Upload database from client "MOM" (later considered DIRTY)
		clientMOM.createNewFile("fileMOM-1.jpg");
		clientMOM.up(upOptionsWithForce);

		clientMOM.createNewFile("fileMOM-2.jpg");
		clientMOM.up(upOptionsWithForce);

		// 5. Download changes from "MOM" (apply databases and files that will later be DIRTY)
		downOperationResult = clientIH.down();
		assertEquals(0, downOperationResult.getDirtyDatabasesCreated().size());

		// 6. Rename hidden database (= the later winner!)Now download the changes that
		//    Databases of client "MOM" will be considered "DIRTY"
		hSEDatabaseFileHidden.renameTo(hSEDatabaseFile);

		downOperationResult = clientIH.down();
		assertEquals(2, downOperationResult.getDirtyDatabasesCreated().size());

		// 7. This should remove DIRTY database versions from the database
		//    and ADD the multichunks from the previous database versions to the new database version (<< this is what kills MOM)
		clientIH.up();

		clientMOM.down(); // <<<<<<<< This should produce Pim's stack trace from issue #227

		// Tear down
		clientIH.deleteTestData();
		clientMOM.deleteTestData();
		clientHSE.deleteTestData();
	}

	@Test
	public void testIssue227_IH() throws Exception {
		// Setup
		LocalTransferSettings testConnection = (LocalTransferSettings) TestConfigUtil.createTestLocalConnection();

		TestClient clientIH = new TestClient("iH", testConnection);
		TestClient clientMOM = new TestClient("MOM", testConnection);
		TestClient clientHSE = new TestClient("hSE", testConnection);
		TestClient clientMee = new TestClient("Mee", testConnection);

		UpOperationOptions upOptionsWithForce = new UpOperationOptions();
		upOptionsWithForce.setForceUploadEnabled(true);

		clientIH.createNewFile("file1.jpg");
		clientIH.up();

		clientIH.createNewFile("file2.jpg");
		clientIH.up();

		clientMOM.down();
		clientHSE.down();

		// -

		clientMOM.createNewFile("file3.jpg");
		clientMOM.up();

		clientMOM.createNewFile("file4.jpg");
		clientMOM.up();

		clientIH.down();
		clientHSE.down();

		// All in sync

		/*
		 * We want to create a time difference situation here between different clients.
		 *
		 * In reality:
		 * - Client "hSE" uploads a new database AFTER client "MOM"
		 *
		 * In this test:
		 * 1. Client "hSE" uploads a new database BEFORE client "MOM"
		 * 2. We hide "hSE"'s database by moving it to a temp. file
		 *    // ...
		 * 5. When we do 'down' at client "IH", the databases of client "MOM" are considered DIRTY
		 *
		 */

		// 1. Upload new database for hSE
		clientHSE.createNewFile("fileHSE-1.jpg");
		clientHSE.up(upOptionsWithForce);

		File[] hSEDatabaseFiles = new File(testConnection.getPath() + "/databases/").listFiles(new FilenameFilter() {
			public boolean accept(File dir, String name) {
				return name.contains("hSE");
			}
		});

		assertEquals(1, hSEDatabaseFiles.length);

		// 2. Hide database from other clients
		File hSEDatabaseFile = hSEDatabaseFiles[0];
		File hSEDatabaseFileHidden = new File(hSEDatabaseFile.getParentFile(), "HIDE_THIS_FILE_" + hSEDatabaseFile.getName());

		hSEDatabaseFile.renameTo(hSEDatabaseFileHidden);

		// 3. This shouldn't do anything; no new databases!
		DownOperationResult downOperationResult = clientIH.down();
		assertEquals(0, downOperationResult.getDownloadedUnknownDatabases().size());

		// 4. Upload database from client "MOM" (later considered DIRTY)
		clientMOM.createNewFile("fileMOM-1.jpg");
		clientMOM.changeFile("file1.jpg");
		clientMOM.up(upOptionsWithForce);

		clientMOM.createNewFile("fileMOM-2.jpg");
		clientMOM.up(upOptionsWithForce);

		// 5. Download changes from "MOM" (apply databases and files that will later be DIRTY)
		downOperationResult = clientIH.down();
		assertEquals(0, downOperationResult.getDirtyDatabasesCreated().size());

		// 6. Rename hidden database (= the later winner!)Now download the changes that
		//    Databases of client "MOM" will be considered "DIRTY"
		hSEDatabaseFileHidden.renameTo(hSEDatabaseFile);

		downOperationResult = clientIH.down();
		assertEquals(2, downOperationResult.getDirtyDatabasesCreated().size());

		// 7. This should remove DIRTY database versions from the database
		//    and ADD the multichunks from the previous database versions to the new database version (<< this is what kills MOM)
		clientIH.up();

		clientIH.down();
		clientIH.copyFile("file2.jpg", "file2copy.jpg"); // <<< This copies a file for which the filecontent has been deleted
		clientIH.up();

		clientMee.down(); // << This should throw Philipp's stack trace in #227

		// Tear down
		clientIH.deleteTestData();
		clientMOM.deleteTestData();
		clientHSE.deleteTestData();
		clientMee.deleteTestData();
	}


	@Test
	public void testIssue227_multiple_resolve() throws Exception {
		// Setup
		LocalTransferSettings testConnection = (LocalTransferSettings) TestConfigUtil.createTestLocalConnection();

		TestClient clientIH = new TestClient("iH", testConnection);
		TestClient clientMOM = new TestClient("MOM", testConnection);
		TestClient clientHSE = new TestClient("hSE", testConnection);
		TestClient clientMee = new TestClient("Mee", testConnection);
		TestClient clientIHtwo = new TestClient("IHtwo", testConnection);

		UpOperationOptions upOptionsWithForce = new UpOperationOptions();
		upOptionsWithForce.setForceUploadEnabled(true);

		clientIH.createNewFile("file1.jpg");
		clientIH.up();

		clientIH.createNewFile("file2.jpg");
		clientIH.up();

		clientMOM.down();
		clientHSE.down();

		// -

		clientMOM.createNewFile("file3.jpg");
		clientMOM.up();

		clientMOM.createNewFile("file4.jpg");
		clientMOM.up();

		clientIH.down();
		clientIHtwo.down();
		clientHSE.down();

		// All in sync

		/*
		 * We want to create a time difference situation here between different clients.
		 *
		 * In reality:
		 * - Client "hSE" uploads a new database AFTER client "MOM"
		 *
		 * In this test:
		 * 1. Client "hSE" uploads a new database BEFORE client "MOM"
		 * 2. We hide "hSE"'s database by moving it to a temp. file
		 *    // ...
		 * 5. When we do 'down' at client "IH", the databases of client "MOM" are considered DIRTY
		 *
		 */

		// 1. Upload new database for hSE
		clientHSE.createNewFile("fileHSE-1.jpg");
		clientHSE.up(upOptionsWithForce);

		File[] hSEDatabaseFiles = new File(testConnection.getPath() + "/databases/").listFiles(new FilenameFilter() {
			public boolean accept(File dir, String name) {
				return name.contains("hSE");
			}
		});

		assertEquals(1, hSEDatabaseFiles.length);

		// 2. Hide database from other clients
		File hSEDatabaseFile = hSEDatabaseFiles[0];
		File hSEDatabaseFileHidden = new File(hSEDatabaseFile.getParentFile(), "HIDE_THIS_FILE_" + hSEDatabaseFile.getName());

		hSEDatabaseFile.renameTo(hSEDatabaseFileHidden);

		// 3. This shouldn't do anything; no new databases!
		DownOperationResult downOperationResult = clientIH.down();
		assertEquals(0, downOperationResult.getDownloadedUnknownDatabases().size());

		clientIHtwo.down(); // same as IH!

		// 4. Upload database from client "MOM" (later considered DIRTY)
		clientMOM.createNewFile("fileMOM-1.jpg");
		clientMOM.changeFile("file1.jpg");
		clientMOM.up(upOptionsWithForce);

		clientMOM.createNewFile("fileMOM-2.jpg");
		clientMOM.up(upOptionsWithForce);

		// 5. Download changes from "MOM" (apply databases and files that will later be DIRTY)
		downOperationResult = clientIH.down();
		assertEquals(0, downOperationResult.getDirtyDatabasesCreated().size());

		clientIHtwo.down(); // same as IH!

		// 6. Rename hidden database (= the later winner!)Now download the changes that
		//    Databases of client "MOM" will be considered "DIRTY"
		hSEDatabaseFileHidden.renameTo(hSEDatabaseFile);

		downOperationResult = clientIH.down();
		assertEquals(2, downOperationResult.getDirtyDatabasesCreated().size());

		clientIHtwo.down(); // same as IH!

		// 7. This should remove DIRTY database versions from the database
		//    and ADD the multichunks from the previous database versions to the new database version (<< this is what kills MOM)
		clientIH.up(upOptionsWithForce);

		clientIHtwo.up(upOptionsWithForce);

		/*clientIH.down();
		clientIH.copyFile("file2.jpg", "file2copy.jpg"); // <<< This copies a file for which the filecontent has been deleted
		clientIH.up();*/

		clientMee.down(); // << This should throw Philipp's stack trace in #227

		// Tear down
		clientIH.deleteTestData();
		clientMOM.deleteTestData();
		clientHSE.deleteTestData();
		clientMee.deleteTestData();
		clientIHtwo.deleteTestData();
	}
}
