package fr.umlv.tpnotesept;

import static org.junit.Assert.*;

import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

import org.junit.Test;

import fr.umlv.tpnotesept.Logger.Log;

public class LoggerTest {
  @Test(expected=NullPointerException.class)
  public void testLogger() {
    new Logger(null);
  }
  
  @Test(expected=NullPointerException.class)
  public void testLogger2() throws IOException {
    new Logger(null, Charset.defaultCharset());
  }
  
  @Test(expected=IllegalStateException.class)
  public void testGetLog() {
    Logger logger = new Logger(new StringWriter());
    logger.getLog();
  }
  
  @Test
  public void testGetLog2() {
    Logger logger = new Logger(new StringWriter());
    Log log = logger.createLog();
    Log log2 = logger.getLog();
    assertSame(log, log2);
  }
  
  @Test
  public void testLog() throws IOException {
    Logger logger = new Logger(new StringWriter());
    Log log = logger.createLog();
    log.close();
    try {
      log.log("foo");
      fail();
    } catch(IllegalStateException e) {
      // ok
    }
  }
  
  @Test
  public void testLog2() throws IOException {
    Logger logger = new Logger(new StringWriter());
    Log log = logger.createLog();
    logger.close();
    try {
      log.log("foo");
      fail();
    } catch(IllegalStateException e) {
      // ok
    }
  }
  
  @Test
  public void testLog3() throws IOException, InterruptedException {
    StringWriter writer = new StringWriter();
    final Logger logger = new Logger(writer);
    Thread thread = new Thread(new Runnable() {
      
      @Override
      public void run() {
        Log log = logger.createLog();
        for(int i=0; i<10; i++) {
          try {
            Thread.sleep(50);
          } catch (InterruptedException e) {
            // do nothing
          }
          log.log("1");
        }
        try {
          log.close();
        } catch (IOException e) {
          throw new AssertionError(e);
        }
      }
    });
    thread.start();
    
    Log log = logger.createLog();
    for(int i=0; i<10; i++) {
      try {
        Thread.sleep(50);
      } catch (InterruptedException e) {
        // do nothing
      }
      log.log("2");
    }
    log.close();
    
    thread.join();
    logger.close();
    
    String message = writer.toString();
    assertFalse(message.matches("1.*2.*1"));
    assertFalse(message.matches("2.*1.*2"));
    //System.out.println(message);
  }
  
  @Test
  public void testLog4() throws IOException {
    Path path = Files.createTempFile(null, null);
    Charset defaultCharset = Charset.defaultCharset();
    Logger logger = new Logger(path, defaultCharset);
    Log log = logger.createLog();
    log.log("foobar");
    log.close();
    logger.close();
    List<String> lines = Files.readAllLines(path, defaultCharset);
    assertEquals("foobar", lines.get(0));
  }
  
  @Test
  public void testCreateLogByAnotherThread() {
    final Logger logger = new Logger(new StringWriter());
    new Thread(new Runnable() {
      @Override
      public void run() {
        logger.createLog();
      }
    }).start();
  }
  
  @Test
  public void testGetLogByAnotherThread() throws IOException, InterruptedException {
    final Logger logger = new Logger(new StringWriter());
    new Thread(new Runnable() {
      @Override
      public void run() {
        try {
          logger.getLog();
          fail();
        } catch(IllegalStateException e) {
          // ok
        }
      }
    }).start();
  }
  
  @Test
  public void testLogByAnotherThread() throws IOException {
    Logger logger = new Logger(new StringWriter());
    final Log log = logger.createLog();
    new Thread(new Runnable() {
      @Override
      public void run() {
        try {
          log.log("foo");
          fail();
        } catch(IllegalStateException e) {
          // ok
        }
      }
    }).start();
  }
  
  @Test
  public void testLogCloseByAnotherThread() throws IOException, InterruptedException {
    Logger logger = new Logger(new StringWriter());
    final Log log = logger.createLog();
    Thread thread = new Thread(new Runnable() {
      @Override
      public void run() {
        try {
          log.close();
        } catch (IOException e) {
          throw new AssertionError(e);
        }
      }
    });
    thread.start();
    thread.join();
    assertTrue(log.isClosed());
    try {
      log.log("foo");
      fail();
    } catch(IllegalStateException e) {
      // ok
    }
    logger.close();
  }
  
  @Test
  public void testLoggerCloseByAnotherThread() throws IOException, InterruptedException {
    final Logger logger = new Logger(new StringWriter());
    Log log = logger.createLog();
    Thread thread = new Thread(new Runnable() {
      @Override
      public void run() {
        try {
          logger.close();
        } catch (IOException e) {
          throw new AssertionError(e);
        }
      }
    });
    thread.start();
    thread.join();
    assertTrue(log.isClosed());
    try {
      log.log("foo");
      fail();
    } catch(IllegalStateException e) {
      // ok
    }
    logger.close();  // can be called twice
  }
  
  @Test
  public void testLogIsClosed() throws IOException {
    Logger logger = new Logger(new StringWriter());
    Log log = logger.createLog();
    assertFalse(log.isClosed());
    log.close();
    assertTrue(log.isClosed());
  }
  
  @Test
  public void testLogIsClosed2() throws IOException {
    Logger logger = new Logger(new StringWriter());
    Log log = logger.createLog();
    logger.close();
    assertTrue(log.isClosed());
  }
  
  @Test
  public void testLoggerClose() throws IOException {
    Logger logger = new Logger(new StringWriter());
    try {
      // do something
    } finally {
      logger.close();
    }
  }
}
