package com.freetymekiyan.algorithms.level.medium;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import java.util.ArrayDeque;
import java.util.Deque;

/**
 * Suppose we abstract our file system by a string in the following manner:
 * <p>
 * The string "dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext" represents:
 * <p>
 *  dir
 *      subdir1
 *      subdir2
 *          file.ext
 * The directory dir contains an empty sub-directory subdir1 and a sub-directory subdir2 containing a file file.ext.
 * <p>
 * The string "dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdir1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext" represents:
 * <p>
 *  dir
 *      subdir1
 *      file1.ext
 *          subsubdir1
 *      subdir2
 *          subsubdir2
 *              file2.ext
 * The directory dir contains two sub-directories subdir1 and subdir2. subdir1 contains a file file1.ext and an empty
 * second-level sub-directory subsubdir1. subdir2 contains a second-level sub-directory subsubdir2 containing a file
 * file2.ext.
 * <p>
 * We are interested in finding the longest (number of characters) absolute path to a file within our file system. For
 * example, in the second example above, the longest absolute path is "dir/subdir2/subsubdir2/file2.ext", and its length
 * is 32 (not including the double quotes).
 * <p>
 * Given a string representing the file system in the above format, return the length of the longest absolute path to
 * file in the abstracted file system. If there is no file in the system, return 0.
 * <p>
 * Note:
 * The name of a file contains at least a . and an extension.
 * The name of a directory or sub-directory will not contain a ..
 * Time complexity required: O(n) where n is the size of the input string.
 * <p>
 * Notice that a/aa/aaa/file1.txt is not the longest file path, if there is another path aaaaaaaaaaaaaaaaaaaaa/sth.png.
 */
public class LongestAbsoluteFilePath {

    private LongestAbsoluteFilePath l;

    /**
     * Stack.
     * Using stack to save previous length at each level.
     * Find current level by the last index of "\t" in filename + 1.
     * Compare current level with stack size, stack size should be one level larger than current level.
     * Calculate current length, with slash at the end.
     * Push this length into stack.
     * If current name is a file, update max.
     * The length here should minus 1 to remove the last "/".
     */
    public int lengthLongestPath(String input) {
        Deque<Integer> stack = new ArrayDeque<>();
        stack.push(0); // Dummy level, start from 0
        String[] files = input.split("\n");
        int max = 0;
        for (String f : files) {
            int level = f.lastIndexOf("\t") + 1;
            while (stack.size() > level + 1) { // Stack size is one level larger since we have a dummy level
                stack.pop();
            }
            int len = stack.peek() + f.length() - level + 1; // Previous length + current length + "/"
            stack.push(len);
            if (f.contains(".")) { // Update max only when it is a file
                max = Math.max(max, len - 1); // Length without the last "/"
            }
        }
        return max;
    }

    @Before
    public void setUp() {
        l = new LongestAbsoluteFilePath();
    }

    @Test
    public void testExamples() {
        Assert.assertEquals(20, l.lengthLongestPath("dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext"));
        Assert.assertEquals(32, l.lengthLongestPath("dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdir1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext"));
    }

    @After
    public void tearDown() {
        l = null;
    }

}

add Longest Absolute File Path