const std = @import("std");
const print = std.debug.print;
var buffer : [30000]u8 = undefined;
var debugBuffer : [30000]u64 = undefined;

const Dir = enum {leftUp, Up, rightUp, left, right, downLeft, down, downRight};

const GenericError = error{
    NoNewlineFound,
    HitBoundary,
};
var debugFile : std.fs.File = undefined;

pub fn main() ! void
{

    debugFile = try std.fs.cwd().createFile("debug.log",.{});
    defer debugFile.close();

    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const allocator = arena.allocator();

    var absPos : usize = 0;
   // var currentPos : usize = 0;
    var found : u64 = 0;
    var ret : u64 = 0;

    const read_bytes = try readFile("day4.txt");
    const lineLen = try getLineLen();

    for(buffer[0..read_bytes],0..) | _,currentPos |
    {
        absPos = currentPos;
        print("absolute Pos: {}\n",.{absPos});
       // if(@mod(currentPos+1,lineLen) != 0 or currentPos == 0)
       // {
            print("mod: {}\n",.{@mod(currentPos,lineLen)});
            //ret = try checkString(allocator,"XMAS",&absPos,lineLen);
            ret = try checkForMas(allocator,&absPos,lineLen);
            debugBuffer[currentPos] = ret;
            found = found + ret;
            print("Found: {}\n",.{ret});
        //}

    }
    print("AllFound: {}\n",.{found});

    for(debugBuffer[0..read_bytes],0..) | buf,i |
    {
        if(buf == 0)
        {
            if(@mod(i,lineLen-1) != 0)
            {
                print(".",.{});
            }

        }
        else
        {
            print("{d}",.{buf});
        }
        if(@mod(i+1,lineLen) == 0)
        {
            print("\n",.{});
        }
        
    }
}

fn checkString(allocator : std.mem.Allocator, pattern : [] const u8,pos : *usize,lineLen : u64) !u64
{
    var result : u64 = 0;
    const tempPos = pos.*;
        inline for (@typeInfo(Dir).Enum.fields) | enu | 
        {
            pos.* = tempPos;
            //print("typeof:{s} {}\n",.{enu.name,i});
            const slice = move(allocator,@enumFromInt(enu.value),pattern.len-1,pos,lineLen) catch "";
                print("pattern: {s} slice {s} {s}\n",.{pattern,slice,enu.name});
                if(std.mem.eql(u8,pattern,slice) == true)
                {
                    result +=1;
                }
        }

        return result;
}

fn checkForMas(allocator : std.mem.Allocator,pos : *usize,lineLen : u64) !u64
{
    var result : u64 = 0;
    const tempPos = pos.*;

    pos.* = tempPos;
    if(buffer[pos.*] == 'A')
    {
        const slice = move(allocator,Dir.leftUp,1,pos,lineLen) catch "..";
    pos.* = tempPos;
        const slice2 = move(allocator,Dir.downRight,1,pos,lineLen) catch "..";
    pos.* = tempPos;

        const slice3 = move(allocator,Dir.rightUp,1,pos,lineLen) catch "..";
    pos.* = tempPos;
        const slice4 = move(allocator,Dir.downLeft,1,pos,lineLen) catch "..";
    pos.* = tempPos;
        
        print("slice: {s}\n",.{slice});
        print("slice2: {s}\n",.{slice2});
        print("slice3: {s}\n",.{slice3});
        print("slice4: {s}\n",.{slice4});
        
        if(((slice3[1] == 'M' and slice4[1] == 'S') or (slice3[1] == 'S' and slice4[1] == 'M')) and ((slice[1] == 'M' and slice2[1] == 'S') or (slice[1] == 'S' and slice2[1] == 'M')))
        {
            result += 1;
        }


    }
        return result;
}

fn move(allocator : std.mem.Allocator, dir : Dir, count: usize,absPos : *usize,lineLen : u64) ![]u8
{
    var result : []u8  = try allocator.alloc(u8,1);

    result[0] = buffer[absPos.*];
    //defer allocator.free(result);

    for(0..count) | _ |
    {
        const homePos = absPos.*;
        //debugBuffer[absPos.*] = '.';
    if(absPos.* != '\n')
    {
        switch(dir) {
            Dir.leftUp => {
                if(absPos.* > lineLen)
                {
                    absPos.* = absPos.* - lineLen - 1;
                }
                else
                {
                    return error.HitBoundary;
                }
            },
            Dir.Up => {
                if(absPos.* > lineLen)
                {
                        absPos.* = absPos.* - lineLen;
                        print("dir.up {c}\n",.{buffer[absPos.*]});
                }
                else
                {
                    return error.HitBoundary;
                }
            },
            Dir.rightUp => {
                if(absPos.* > lineLen)
                {
                        absPos.* = absPos.* - lineLen + 1 ;
                }
                else
                {
                    return error.HitBoundary;
                }
            },
            Dir.left => {
                //if(@mod(absPos.*,lineLen)!=0)
                if(absPos.* > 0)
                {
                    absPos.* = absPos.* - 1;
                }
                else
                {
                    return error.HitBoundary;
                }
            },
            Dir.right => {
                    absPos.* = absPos.* + 1;
            },
            Dir.downLeft => {
                    absPos.* = absPos.* + lineLen - 1;
            },
            Dir.down => {
                    absPos.* = absPos.* + lineLen;
            },
            Dir.downRight => {
                    absPos.* = absPos.* + lineLen + 1;
            },
        }
    }
        if(buffer[absPos.*] != '\n')
        {
            result = try std.fmt.allocPrint(allocator,"{s}{c}",.{result,buffer[absPos.*]});
        }
        else
        {
            absPos.* = homePos;
            result = try std.fmt.allocPrint(allocator,"...",.{});
        }

    }
    //print("absPos: {} lineLen: {}\n",.{absPos.*,lineLen});
    return result;
}


fn getLineLen() GenericError!usize
{
    if(std.mem.indexOf(u8,&buffer,"\n")) | index |
    {
        print("found newline at {?}\n",.{index});
        //add for the newline itself
        return index+1;
    }
    else
    {
        return error.NoNewlineFound;
    }
}

fn readFile(filename : [] const u8) !usize
{
    const file = try std.fs.cwd().openFile(filename,.{});
    defer file.close();

    const bytes_read = try file.readAll(&buffer);
    return bytes_read;

}

