/*
 * 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 static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.syncany.tests.util.TestAssertUtil.assertFileListEquals;
import static org.syncany.tests.util.TestAssertUtil.assertSqlDatabaseEquals;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.junit.Test;
import org.syncany.database.SqlDatabase;
import org.syncany.operations.cleanup.CleanupOperationOptions;
import org.syncany.plugins.transfer.TransferSettings;
import org.syncany.tests.util.TestClient;
import org.syncany.tests.util.TestConfigUtil;

public class FileVanishedScenarioTest {
	private static final Logger logger = Logger.getLogger(FileVanishedScenarioTest.class.getSimpleName());

	// TODO [low] If a file has vanished, are its chunks and multichunks still added to the database, and then uploaded? If so, fix this!

	@Test
	public void testCallUpWhileDeletingFiles() throws Exception {
		// Setup
		final TransferSettings testConnection = TestConfigUtil.createTestLocalConnection();
		final TestClient clientA = new TestClient("A", testConnection);
		final TestClient clientB = new TestClient("B", testConnection);
		final int numFiles = 100;
		final int numFilesVanished = 50;
		final int numFilesRemaining = numFiles - numFilesVanished;
		final int sizeFiles = 500 * 1024;
		CleanupOperationOptions options = new CleanupOperationOptions();
		options.setMinKeepSeconds(0);

		// Prepare by creating test files
		logger.log(Level.INFO, "Creating test files ...");

		for (int i = 0; i <= numFiles; i++) {
			clientA.createNewFile("A-original" + i, sizeFiles);
		}

		// Prepare threads (delete & run up)
		Thread deleteFilesThread = new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = numFiles; i >= numFilesVanished; i--) {
					boolean deleteSuccess = clientA.deleteFile("A-original" + i);

					if (deleteSuccess) {
						logger.log(Level.SEVERE, "Deleted " + clientA.getLocalFile("A-original" + i));
					}
					else {
						logger.log(Level.SEVERE, "FAILED TO DELETE FILE " + clientA.getLocalFile("A-original" + i));
					}

					try {
						Thread.sleep(50);
					}
					catch (InterruptedException e) {
						logger.log(Level.FINE, "Thread interrupted", e);
					}
				}
			}
		}, "A-delete");

		Thread runUpThread = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					Thread.sleep(40);
					clientA.up();
				}
				catch (Exception e) {
					e.printStackTrace();
					fail(e.getMessage());
				}
			}
		}, "A-up");

		// Before we start: init database (this takes a while)
		clientA.status();
		clientB.status();

		// Delete files and run up simultaneously
		// --> This will hopefully lead to a couple of 'vanished' files
		logger.log(Level.INFO, "Starting 'up' thread ...");
		runUpThread.start();

		logger.log(Level.INFO, "Starting 'delete' thread ...");
		deleteFilesThread.start();

		runUpThread.join();
		deleteFilesThread.join();

		// Test 1: There should be between 50 and 100 file histories in the database
		SqlDatabase databaseClientA = clientA.loadLocalDatabase();

		assertTrue("There should be less file histories than originally added files.",
				databaseClientA.getFileHistoriesWithFileVersions().size() < numFiles);
		assertTrue("There should be more (or equal size) file histories than files there are.", databaseClientA.getFileHistoriesWithFileVersions()
				.size() >= numFilesRemaining);

		// Test 2: Now up the rest, there should be exactly 50 files in the database
		clientA.up();
		clientA.cleanup(options);

		databaseClientA = clientA.loadLocalDatabase();
		assertEquals("There should be EXACTLY " + numFilesRemaining + " file histories in the database.", numFilesRemaining, databaseClientA
				.getFileHistoriesWithFileVersions().size());

		// Test 3: After that, the sync between the clients should of course still work
		clientB.down();
		assertFileListEquals("Files of both clients should be identical.", clientA.getLocalFilesExcludeLockedAndNoRead(),
				clientB.getLocalFilesExcludeLockedAndNoRead());
		assertSqlDatabaseEquals(clientA.getDatabaseFile(), clientB.getDatabaseFile());

		// Tear down
		clientA.deleteTestData();
		clientB.deleteTestData();
	}

	@Test
	public void testFolderVanishesWhenSyncingDown() throws Exception {
		// Setup
		TransferSettings testConnection = TestConfigUtil.createTestLocalConnection();
		TestClient clientA = new TestClient("A", testConnection);
		TestClient clientB = new TestClient("B", testConnection);

		// A
		clientA.createNewFolder("folder1");
		clientA.up();

		// B
		clientB.down();
		assertFileListEquals(clientA.getLocalFilesExcludeLockedAndNoRead(), clientB.getLocalFilesExcludeLockedAndNoRead());

		// A
		clientA.createNewFile("folder1/file1");
		clientA.up();

		// B
		clientB.deleteFile("folder1");
		assertFalse(clientB.getLocalFile("folder1").exists());

		clientB.down();
		assertFileListEquals(clientA.getLocalFilesExcludeLockedAndNoRead(), clientB.getLocalFilesExcludeLockedAndNoRead());

		// Tear down
		clientA.deleteTestData();
		clientB.deleteTestData();
	}
}
