// File: /workspace/github_c_makefile_star2_O3/Sidd0107[P]EXT2-File-System/ext2_cp.c
// Function: main
int main(int argc, char **argv) {
    //Checks that valid no of arguments are provided
    if(argc != 4) {
        fprintf(stderr, "Usage: ext2_cp <image file name> <file path on local disk> <file path on ext2 image>\n");
        exit(1);
    }
    // Gets details from command args
	char *dest_filename = extract_filename(argv[3]);
	char *dest_directory = strrchr(argv[3], '/');
	if (!dest_directory){
	  fprintf(stderr, "destination directory does not exist");
        return ENOENT;
	}
    //Open's file to be copied
	int fd = open(argv[1], O_RDWR);
    disk = mmap(NULL, 128 * BLOCK_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if(disk == MAP_FAILED) {
		perror("mmap");
		exit(1);
    }
    //Gets super block and group descriptor
	struct ext2_super_block *sb = (struct ext2_super_block *)(disk + BLOCK_SIZE);
	struct ext2_group_desc *gd = (struct ext2_group_desc *)  (disk + 2048 + ((sb -> s_block_group_nr) *  sizeof(struct ext2_group_desc)));
	//Gets file info
	struct stat fileinfo;
	if (stat(argv[2], &fileinfo) == -1){
		free(dest_filename);
		perror("stat for input file failed");
		exit(1);
	}
	
	dest_directory[(dest_directory == argv[3])? 1: 0] = '\0';
	//Checks if destination exists
	if(file_exists(argv[3], get_block(disk, gd -> bg_inode_table, BLOCK_SIZE), dest_filename)){
		free(dest_filename);
		fprintf(stderr, "cp: file exists");
		return -1;
	}
    
	//should be safe 
	unsigned int file_size = fileinfo.st_size;
	//equivalent to ceiling of file_size / 1024
	int num_blocks_required = (file_size > 0) ? ((file_size-1) >> 10) + 1 + ((file_size > BLOCK_SIZE * 12)? 1:0)  : 0;
	//CHECK FOR INODE TO RESERVE
	int free_inode;
	if((free_inode = find_first_free_bit(get_block(disk, gd -> bg_inode_bitmap, BLOCK_SIZE), 0, 128)) == -1){
		free(dest_filename);
		fprintf(stderr, "cp: operation failed: No free inodes");
		exit(1);
	}
	//CHECK FOR BLOCKS TO RESERVE
	if(gd -> bg_free_inodes_count == 0){
		free(dest_filename);
		fprintf(stderr, "Insufficient free inodes for file.");
		exit(1);
	}
    //Loops through iblocks of directory to make sure there is enough space
	int next_free = 0, free_block_counter = 0, num_free_blocks = 0, i;
	unsigned int inodes_to_reserve[num_blocks_required];
	while(num_free_blocks < num_blocks_required){
		if((next_free = find_first_free_bit(get_block(disk, gd -> bg_block_bitmap, BLOCK_SIZE),
												free_block_counter, 128)) == -1){
			free(dest_filename);
			fprintf(stderr, "Insufficient free space for file.");
			exit(1);
		}
		inodes_to_reserve[num_free_blocks++] = next_free;
		free_block_counter = next_free + 1;
	}
	if(num_free_blocks != num_blocks_required){
		free(dest_filename);
		fprintf(stderr, "Insufficient free space for file.");
		exit(1);
	}
	
	unsigned int filename_length = strlen(dest_filename);
	unsigned int dir_entry_size = (8 + filename_length) + (4 - (8 + filename_length) % 4) % 4;

	
	//CHECK FOR DIRECTORY TO RESERVE
	struct ext2_dir_entry_2  * new_file = malloc(dir_entry_size);
	if(!new_file){
		perror("malloc");
		exit(1);
	}
	new_file -> inode = free_inode + 1;
	new_file -> name_len = strlen(dest_filename);
	new_file -> rec_len = dir_entry_size;
	new_file -> file_type = 1;
	memcpy(new_file -> name, dest_filename, strlen(dest_filename));
	int new_capacity = gd -> bg_free_blocks_count - num_blocks_required;

    
	struct basic_fileinfo destination = find_file(argv[3], get_block(disk, gd -> bg_inode_table, BLOCK_SIZE));
    //Checks to see if destination is suitable
    //(directory, correct inode num(not 2 and >11))
	if(destination.inode < 11 && destination.inode != 2){
		free(dest_filename);
		fprintf(stderr, "Invalid destination");
		free(new_file);
		exit(1);
	}
	if(destination.type != 'd'){
		free(dest_filename);
		fprintf(stderr, "cp: destination is not a directory");
		free(new_file);
		exit(1);
	}
    // Reserves spot for and adds this entry into the directory
    // for the new file(error if not enough space)
	if ((new_capacity = reserve_directory_entry(get_block(disk, gd -> bg_inode_table, BLOCK_SIZE), destination.inode, new_capacity, new_file, get_block(disk, gd -> bg_block_bitmap, BLOCK_SIZE))) == -1){
		free(dest_filename);
		fprintf(stderr, "cp: Not enough capacity for file");
		exit(-1);
	}
	
	free(new_file);
	//reduces free inode count as new file takes up a new inode
	gd -> bg_free_inodes_count --;
	//Sets inode details for new file.
	set_inode(get_inode(get_block(disk, gd -> bg_inode_table, BLOCK_SIZE), free_inode + 1),
	free_inode, file_size, inodes_to_reserve, 
	num_blocks_required, 
	get_block(disk, gd -> bg_block_bitmap, BLOCK_SIZE),
	get_block(disk, gd -> bg_inode_bitmap, BLOCK_SIZE),
	EXT2_S_IFREG);
	gd -> bg_free_blocks_count = new_capacity;
    //Opens file for copying
	int copyfd = open(argv[2], O_RDONLY);
	if(copyfd == -1){
		perror("failed to open input file");
	}
    //Copies the file content into the new destination file
	int bytes_read = 0, current_bytes_read;
	for(i = 0; i < ((num_blocks_required <= 12)? num_blocks_required : num_blocks_required - 1); i++){
		char *current_block = get_block_from_inode(get_block(disk, gd -> bg_inode_table, BLOCK_SIZE), free_inode + 1, i);
		if ((current_bytes_read = read(copyfd, current_block, BLOCK_SIZE)) <= 0){
			perror("read");
			exit(1);
		}
		bytes_read += current_bytes_read;
	}
	printf("Success copying %s: copied %d bytes\n", dest_filename, bytes_read);
	close(copyfd);
	free(dest_filename);
	return 0;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0xd8, %rsp
cmpl $0x4, %edi
jne 0x246e
movq %rsi, %r12
movq 0x18(%rsi), %rdi
callq 0x1dd5
movq %rax, %r15
movq 0x18(%r12), %rdi
movl $0x2f, %esi
callq 0x10a0
testq %rax, %rax
je 0x2432
movq %rax, %rbx
movq 0x8(%r12), %rdi
movl $0x2, %esi
xorl %eax, %eax
callq 0x1100
movl $0x20000, %esi          # imm = 0x20000
xorl %edi, %edi
movl $0x3, %edx
movl $0x1, %ecx
movl %eax, %r8d
xorl %r9d, %r9d
callq 0x1080
movq %rax, 0x306d(%rip)      # 0x50b8
cmpq $-0x1, %rax
je 0x2473
movq %rax, %r14
movzwl 0x45e(%rax), %r13d
movq 0x10(%r12), %rdi
leaq -0xf8(%rbp), %rsi
callq 0x10d0
cmpl $-0x1, %eax
je 0x2478
shll $0x5, %r13d
xorl %eax, %eax
cmpq 0x18(%r12), %rbx
sete %al
movb $0x0, (%rbx,%rax)
movq 0x18(%r12), %rbx
movq 0x3020(%rip), %rdi      # 0x50b8
movl 0x808(%r14,%r13), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x18aa
movq %rbx, %rdi
movq %rax, %rsi
movq %r15, %rdx
callq 0x1538
testl %eax, %eax
jne 0x243f
movl -0xc8(%rbp), %eax
testl %eax, %eax
movq %rax, -0x68(%rbp)
je 0x20e0
leal -0x1(%rax), %ebx
shrl $0xa, %ebx
cmpl $0x3001, %eax           # imm = 0x3001
sbbl $-0x1, %ebx
incl %ebx
jmp 0x20e2
xorl %ebx, %ebx
movq 0x2fcf(%rip), %rdi      # 0x50b8
movl 0x804(%r14,%r13), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x18aa
movq %rax, %rdi
xorl %esi, %esi
movl $0x80, %edx
callq 0x1a93
movq %rax, -0x58(%rbp)
cmpl $-0x1, %eax
je 0x2480
movq %r12, -0x50(%rbp)
movq %r15, -0x30(%rbp)
cmpw $0x0, 0x80e(%r14,%r13)
je 0x2488
movq %rsp, -0x60(%rbp)
movl %ebx, %r12d
movq %rsp, %rcx
leaq 0xf(,%r12,4), %rax
andq $-0x10, %rax
subq %rax, %rcx
movq %rcx, -0x40(%rbp)
movq %rcx, %rsp
movl %ebx, -0x34(%rbp)
testl %ebx, %ebx
je 0x21a3
xorl %ebx, %ebx
xorl %r15d, %r15d
movq 0x2f56(%rip), %rdi      # 0x50b8
movl 0x800(%r14,%r13), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x18aa
movq %rax, %rdi
movl %r15d, %esi
movl $0x80, %edx
callq 0x1a93
cmpl $-0x1, %eax
je 0x2460
movl %eax, %r15d
movq -0x40(%rbp), %rax
movl %r15d, (%rax,%rbx,4)
incq %rbx
incl %r15d
cmpq %rbx, %r12
jne 0x215b
movq -0x30(%rbp), %rbx
movq %rbx, %rdi
callq 0x1070
movl %eax, %ecx
negl %ecx
andl $0x3, %ecx
leal (%rax,%rcx), %r12d
addl $0x8, %r12d
movq %r12, %rdi
callq 0x10f0
testq %rax, %rax
je 0x2491
movq %rax, %r15
movq -0x58(%rbp), %rax
incl %eax
movl %eax, -0x38(%rbp)
movl %eax, (%r15)
movq %rbx, %rdi
callq 0x1070
movb %al, 0x6(%r15)
movw %r12w, 0x4(%r15)
movb $0x1, 0x7(%r15)
movq %r15, %r12
addq $0x8, %r12
movq %rbx, %rdi
callq 0x1070
movq %r12, %rdi
movq %rbx, %rsi
movq %rax, %rdx
callq 0x10e0
movzwl 0x80c(%r14,%r13), %ebx
movq -0x50(%rbp), %rax
movq 0x18(%rax), %r12
movq 0x2e8f(%rip), %rdi      # 0x50b8
movl 0x808(%r14,%r13), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x18aa
movq %r12, %rdi
movq %rax, %rsi
callq 0x156e
movq %rax, %r12
shrq $0x20, %r12
cmpq $0xb, %r12
setae %cl
cmpq $0x2, %r12
sete %dl
orb %cl, %dl
je 0x2496
cmpb $0x64, %al
movl -0x34(%rbp), %eax
jne 0x24a2
subl %eax, %ebx
movq 0x2e41(%rip), %rdi      # 0x50b8
movl 0x808(%r14,%r13), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x18aa
movq %rax, -0x48(%rbp)
movq 0x2e24(%rip), %rdi      # 0x50b8
movl 0x800(%r14,%r13), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x18aa
movq -0x48(%rbp), %rdi
movl %r12d, %esi
movl %ebx, %edx
movq %r15, %rcx
movq %rax, %r8
callq 0x1c76
movl %eax, -0x48(%rbp)
cmpl $-0x1, %eax
je 0x24ae
movq %r15, %rdi
callq 0x1030
decw 0x80e(%r14,%r13)
movq 0x2dda(%rip), %rdi      # 0x50b8
movl 0x808(%r14,%r13), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x18aa
movq %rax, %rdi
movl -0x38(%rbp), %esi
callq 0x189a
movq %rax, %r15
movq 0x2db3(%rip), %rdi      # 0x50b8
movl 0x800(%r14,%r13), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x18aa
movq %rax, %r12
movq 0x2d97(%rip), %rdi      # 0x50b8
movl 0x804(%r14,%r13), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x18aa
movq %r15, %rdi
movq -0x58(%rbp), %rsi
movq -0x68(%rbp), %rdx
movq -0x40(%rbp), %rcx
movl -0x34(%rbp), %ebx
movl %ebx, %r8d
movq %r12, %r9
pushq $0x8000                 # imm = 0x8000
pushq %rax
callq 0x1aef
addq $0x10, %rsp
movq %r13, -0x40(%rbp)
movl -0x48(%rbp), %ecx
movw %cx, 0x80c(%r14,%r13)
movq -0x50(%rbp), %rax
movq 0x10(%rax), %rdi
movl %ebx, %r12d
xorl %ebx, %ebx
xorl %esi, %esi
xorl %eax, %eax
callq 0x1100
movl %eax, %r15d
cmpl $-0x1, %eax
je 0x244f
xorl %eax, %eax
cmpl $0xd, %r12d
setge %al
subl %eax, %r12d
testl %r12d, %r12d
jle 0x23f2
movl %r12d, %r13d
xorl %ebx, %ebx
xorl %r12d, %r12d
movq 0x2d0c(%rip), %rdi      # 0x50b8
movq -0x40(%rbp), %rax
movl 0x808(%r14,%rax), %esi
movl $0x400, %edx            # imm = 0x400
callq 0x18aa
movq %rax, %rdi
movl -0x38(%rbp), %esi
movl %r12d, %edx
callq 0x18df
movl $0x400, %edx            # imm = 0x400
movl %r15d, %edi
movq %rax, %rsi
callq 0x10c0
testl %eax, %eax
jle 0x2469
addl %eax, %ebx
incl %r12d
cmpl %r12d, %r13d
jne 0x23a5
leaq 0xe2b(%rip), %rdi       # 0x3224
xorl %r14d, %r14d
movq -0x30(%rbp), %r12
movq %r12, %rsi
movl %ebx, %edx
xorl %eax, %eax
callq 0x1090
movl %r15d, %edi
callq 0x10b0
movq %r12, %rdi
callq 0x1030
movq -0x60(%rbp), %rsp
movl %r14d, %eax
leaq -0x28(%rbp), %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
callq 0x1373
movl $0x2, %r14d
jmp 0x2420
movq %r15, %rdi
callq 0x11c0
movl $0xffffffff, %r14d      # imm = 0xFFFFFFFF
jmp 0x2420
leaq 0xdaf(%rip), %rdi       # 0x3205
callq 0x1110
jmp 0x238c
movq -0x30(%rbp), %rdi
callq 0x11e6
callq 0x124d
callq 0x1198
callq 0x135b
movq %r15, %rdi
callq 0x133e
movq %r15, %rdi
callq 0x1311
movq -0x30(%rbp), %rdi
callq 0x12e4
callq 0x12cc
movq -0x30(%rbp), %rdi
movq %r15, %rsi
callq 0x1292
movq -0x30(%rbp), %rdi
movq %r15, %rsi
callq 0x1213
movq -0x30(%rbp), %rdi
callq 0x1265