/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */

/* $Id$ */

package org.apache.fop.fo.pagination;

import org.junit.Test;
import org.xml.sax.Locator;

import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.FONode;
import org.apache.fop.layoutmgr.BlockLevelEventProducer;

/**
 * Unit Test for PageSequenceMaster
 *
 */
public class PageSequenceMasterTestCase {

    /**
     * Test that block level events are produced in line with
     *  XSL:FO - 6.4.8 fo:page-sequence-master -
     * "It is an error if the entire sequence of sub-sequence-specifiers children is exhausted
     *  while some areas returned by an fo:flow are not placed. Implementations may recover,
     *  if possible, by re-using the sub-sequence-specifier that was last used to generate a page."
     *
     * @throws Exception exception
     */
    @Test
     public void testGetNextSimplePageMasterExhausted() throws Exception {

         //Test when the last sub-sequence specifier is not repeatable
        testGetNextSimplePageMasterExhausted(true);

         //Test when the last sub-sequence specifier is repeatable
        testGetNextSimplePageMasterExhausted(false);

     }

     private void testGetNextSimplePageMasterExhausted(boolean canResume) throws Exception {

         SimplePageMaster spm = mock(SimplePageMaster.class);
         SubSequenceSpecifier mockSinglePageMasterReference
                 = mock(SubSequenceSpecifier.class);
         BlockLevelEventProducer mockBlockLevelEventProducer = mock(BlockLevelEventProducer.class);

         // subject under test
         PageSequenceMaster pageSequenceMaster = createPageSequenceMaster(
                 mockBlockLevelEventProducer);
         pageSequenceMaster.addSubsequenceSpecifier(mockSinglePageMasterReference);

         //Setup to mock the exhaustion of the last sub-sequence specifier
         when(mockSinglePageMasterReference.getNextPageMaster(anyBoolean(), anyBoolean(),
                 anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(null, spm);

         //Need this for the method to return normally
         when(mockSinglePageMasterReference.canProcess(nullable(String.class))).thenReturn(true);

         when(mockSinglePageMasterReference.isReusable()).thenReturn(canResume);

         pageSequenceMaster.getNextSimplePageMaster(false, false, false, false, null, false);

         verify(mockBlockLevelEventProducer).pageSequenceMasterExhausted(nullable(Object.class),
                 nullable(String.class), eq(canResume), nullable(Locator.class));
     }

     /**
      * Test that PageProductionException is thrown if the final simple-page-master
      * cannot handle the main-flow of the page sequence
      * @throws Exception exception
      */
    @Test
      public void testGetNextSimplePageMasterException() throws Exception {

          final String mainFlowRegionName = "main";
          final String emptyFlowRegionName = "empty";

          //  This will represent a page master that does not map to the main flow
          //  of the page sequence
          SimplePageMaster mockEmptySPM = mock(SimplePageMaster.class);
          Region mockRegion = mock(Region.class);
          SinglePageMasterReference mockSinglePageMasterReference
                  = mock(SinglePageMasterReference.class);
          BlockLevelEventProducer mockBlockLevelEventProducer = mock(BlockLevelEventProducer.class);

          LayoutMasterSet mockLayoutMasterSet = mock(LayoutMasterSet.class);
          //The layout master set should return the empty page master
          when(mockLayoutMasterSet.getSimplePageMaster(anyString())).thenReturn(mockEmptySPM);
          when(mockEmptySPM.getRegion(anyInt())).thenReturn(mockRegion);

          when(mockRegion.getRegionName()).thenReturn(emptyFlowRegionName);

          when(mockSinglePageMasterReference.getNextPageMaster(anyBoolean(), anyBoolean(),
                  anyBoolean(), anyBoolean(), anyBoolean()))
                  .thenReturn(null, mockEmptySPM);

          PageSequenceMaster pageSequenceMaster = createPageSequenceMaster(mockLayoutMasterSet,
                  mockBlockLevelEventProducer);

          pageSequenceMaster.startOfNode();
          pageSequenceMaster.addSubsequenceSpecifier(mockSinglePageMasterReference);

          try {
              pageSequenceMaster.getNextSimplePageMaster(false, false, false, false,
                      mainFlowRegionName, false);
              fail("The next simple page master does not refer to the main flow");
         } catch (PageProductionException ppe) {
             //Passed test
         }
      }


     private PageSequenceMaster createPageSequenceMaster(
             BlockLevelEventProducer blockLevelEventProducer) throws FOPException {

         return createPageSequenceMaster(mock(LayoutMasterSet.class), blockLevelEventProducer);
     }

     private PageSequenceMaster createPageSequenceMaster(LayoutMasterSet layoutMasterSet,
             BlockLevelEventProducer blockLevelEventProducer) throws FOPException {
         FONode mockParent = mock(FONode.class);
         Root mockRoot = mock(Root.class);

         //Stub generic components
         when(mockParent.getRoot()).thenReturn(mockRoot);
         when(mockRoot.getLayoutMasterSet()).thenReturn(layoutMasterSet);

         PageSequenceMaster psm =  new PageSequenceMaster(mockParent, blockLevelEventProducer);
         psm.startOfNode();

         return psm;
     }

}

