/**
 * Copyright 2019 Anthony Trinh
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ch.qos.logback.classic;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;

import org.junit.Before;
import org.junit.Test;

import java.util.Map;

import ch.qos.logback.classic.turbo.NOPTurboFilter;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.rolling.helper.FileNamePattern;
import ch.qos.logback.core.status.StatusManager;

public class LoggerContextTest {
  LoggerContext lc;

  @Before
  public void setUp() throws Exception {
    lc = new LoggerContext();
    lc.setName("x");
  }

  @Test
  public void testRootGetLogger() {
    Logger root = lc.getLogger(Logger.ROOT_LOGGER_NAME);
    assertEquals(Level.DEBUG, root.getLevel());
    assertEquals(Level.DEBUG, root.getEffectiveLevel());
  }

  @Test
  public void testLoggerX() {
    Logger x = lc.getLogger("x");
    assertNotNull(x);
    assertEquals("x", x.getName());
    assertNull(x.getLevel());
    assertEquals(Level.DEBUG, x.getEffectiveLevel());
  }

  @Test
  public void testNull() {
    try {
      lc.getLogger((String) null);
      fail("null should cause an exception");
    } catch (IllegalArgumentException e) {
    }
  }

  @Test
  public void testEmpty() {
    Logger empty = lc.getLogger("");
    LoggerTestHelper.assertNameEquals(empty, "");
    LoggerTestHelper.assertLevels(null, empty, Level.DEBUG);

    Logger dot = lc.getLogger(".");
    LoggerTestHelper.assertNameEquals(dot, ".");
    // LoggerTestHelper.assertNameEquals(dot.parent, "");
    // LoggerTestHelper.assertNameEquals(dot.parent.parent, "root");

    // assertNull(dot.parent.parent.parent);
    LoggerTestHelper.assertLevels(null, dot, Level.DEBUG);


    assertEquals(3, lc.getLoggerList().size());
  }

  @Test
  public void testDotDot() {
    Logger dotdot = lc.getLogger("..");
    assertEquals(4, lc.getLoggerList().size());
    LoggerTestHelper.assertNameEquals(dotdot, "..");
    // LoggerTestHelper.assertNameEquals(dotdot.parent, ".");
    // LoggerTestHelper.assertNameEquals(dotdot.parent.parent, "");
    // LoggerTestHelper.assertNameEquals(dotdot.parent.parent.parent, "root");
  }

  int instanceCount() {
    return lc.getLoggerList().size();
  }

  @Test
  public void testLoggerXY() {
    assertEquals(1, lc.getLoggerList().size());

    Logger xy = lc.getLogger("x.y");
    assertEquals(3, instanceCount());
    LoggerTestHelper.assertNameEquals(xy, "x.y");
    LoggerTestHelper.assertLevels(null, xy, Level.DEBUG);

    Logger x = lc.getLogger("x");
    assertEquals(3, instanceCount());

    Logger xy2 = lc.getLogger("x.y");
    assertEquals(xy, xy2);

    Logger x2 = lc.getLogger("x");
    assertEquals(x, x2);
    assertEquals(3, instanceCount());
  }

  @Test
  public void testLoggerMultipleChildren() {
    assertEquals(1, instanceCount());
    Logger xy0 = lc.getLogger("x.y0");
    LoggerTestHelper.assertNameEquals(xy0, "x.y0");

    Logger xy1 = lc.getLogger("x.y1");
    LoggerTestHelper.assertNameEquals(xy1, "x.y1");

    LoggerTestHelper.assertLevels(null, xy0, Level.DEBUG);
    LoggerTestHelper.assertLevels(null, xy1, Level.DEBUG);
    assertEquals(4, instanceCount());

    for (int i = 0; i < 100; i++) {
      Logger xy_i = lc.getLogger("x.y" + i);
      LoggerTestHelper.assertNameEquals(xy_i, "x.y" + i);
      LoggerTestHelper.assertLevels(null, xy_i, Level.DEBUG);
    }
    assertEquals(102, instanceCount());
  }

  @Test
  public void testMultiLevel() {
    Logger wxyz = lc.getLogger("w.x.y.z");
    LoggerTestHelper.assertNameEquals(wxyz, "w.x.y.z");
    LoggerTestHelper.assertLevels(null, wxyz, Level.DEBUG);

    Logger wx = lc.getLogger("w.x");
    wx.setLevel(Level.INFO);
    LoggerTestHelper.assertNameEquals(wx, "w.x");
    LoggerTestHelper.assertLevels(Level.INFO, wx, Level.INFO);
    LoggerTestHelper.assertLevels(null, lc.getLogger("w.x.y"), Level.INFO);
    LoggerTestHelper.assertLevels(null, wxyz, Level.INFO);
  }

  @Test
  public void testStatusWithUnconfiguredContext() {
    Logger logger = lc.getLogger(LoggerContextTest.class);

    for (int i = 0; i < 3; i++) {
      logger.debug("test");
    }

    logger = lc.getLogger("x.y.z");

    for (int i = 0; i < 3; i++) {
      logger.debug("test");
    }

    StatusManager sm = lc.getStatusManager();
    assertTrue("StatusManager has recieved too many messages",
            sm.getCount() == 1);
  }


  @Test
  public void resetTest() {

    Logger root = lc.getLogger(Logger.ROOT_LOGGER_NAME);
    Logger a = lc.getLogger("a");
    Logger ab = lc.getLogger("a.b");

    ab.setLevel(Level.WARN);
    root.setLevel(Level.INFO);
    lc.reset();
    assertEquals(Level.DEBUG, root.getEffectiveLevel());
    assertTrue(root.isDebugEnabled());
    assertEquals(Level.DEBUG, a.getEffectiveLevel());
    assertEquals(Level.DEBUG, ab.getEffectiveLevel());

    assertEquals(Level.DEBUG, root.getLevel());
    assertNull(a.getLevel());
    assertNull(ab.getLevel());
  }

  // http://jira.qos.ch/browse/LBCLASSIC-89
  @Test
  public void turboFilterStopOnReset() {
    NOPTurboFilter nopTF = new NOPTurboFilter();
    nopTF.start();
    lc.addTurboFilter(nopTF);
    assertTrue(nopTF.isStarted());
    lc.reset();
    assertFalse(nopTF.isStarted());
  }

  @Test
  public void resetTest_LBCORE_104() {
    lc.putProperty("keyA", "valA");
    lc.putObject("keyA", "valA");
    assertEquals("valA", lc.getProperty("keyA"));
    assertEquals("valA", lc.getObject("keyA"));
    lc.reset();
    assertNull(lc.getProperty("keyA"));
    assertNull(lc.getObject("keyA"));
  }

  @Test
  public void loggerNameEndingInDotOrDollarShouldWork() {
    {
      String loggerName = "toto.x.";
      Logger logger = lc.getLogger(loggerName);
      assertEquals(loggerName, logger.getName());
    }

    {
      String loggerName = "toto.x$";
      Logger logger = lc.getLogger(loggerName);
      assertEquals(loggerName, logger.getName());
    }
  }

  @Test
  public void levelResetTest() {
    Logger root = lc.getLogger(Logger.ROOT_LOGGER_NAME);
    root.setLevel(Level.TRACE);
    assertTrue(root.isTraceEnabled());
    lc.reset();
    assertFalse(root.isTraceEnabled());
    assertTrue(root.isDebugEnabled());
  }

  @Test
  public void evaluatorMapPostReset() {
    lc.reset();
    assertNotNull(lc.getObject(CoreConstants.EVALUATOR_MAP));
  }

  @SuppressWarnings("unchecked")
  @Test
  public void collisionMapsPostReset() {
    lc.reset();

    Map<String, String> fileCollisions = (Map<String, String>) lc.getObject(CoreConstants.FA_FILENAME_COLLISION_MAP);
    assertNotNull(fileCollisions);
    assertTrue(fileCollisions.isEmpty());

    Map<String, FileNamePattern> filenamePatternCollisionMap = (Map<String, FileNamePattern>) lc.getObject(CoreConstants.RFA_FILENAME_PATTERN_COLLISION_MAP);
    assertNotNull(filenamePatternCollisionMap);
    assertTrue(filenamePatternCollisionMap.isEmpty());

  }

  // http://jira.qos.ch/browse/LOGBACK-142
  @Test
  public void concurrentModification() {
    final int runLen = 100;
    Thread thread = new Thread(new Runnable() {
      public void run() {
        for (int i = 0; i < runLen; i++)  {
          lc.getLogger("a" + i);
          Thread.yield();
        }
      }
    });
    thread.start();

    for (int i = 0; i < runLen; i++) {
      lc.putProperty("a" + i, "val");
      Thread.yield();
    }

  }

}