#define _LARGEFILE64_SOURCE
#define _FILE_OFFSET_BITS 64

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <string.h>
#include <stdlib.h>

#include <linux/usbdevice_fs.h>

int usbreset(const char *filename)
{
	int fd;
	int rc;

	fd = open(filename, O_WRONLY);
	if (fd < 0) {
		perror("Error opening output file");
		return 1;
	}

	printf("Resetting USB device %s\n", filename);
	rc = ioctl(fd, USBDEVFS_RESET, 0);
	if (rc < 0) {
		perror("Error in ioctl");
		return 1;
	}
	printf("Reset successful\n");

	close(fd);
	return 0;
}

off64_t curr_offset = 0;

int copy_loop(const char * src, const char * tgt)
{
	int fd_in = open(src, O_RDONLY);
	int fd_out = 0;
	unsigned char buf[512];
	int rv;

	if (fd_in < 0) {
		fprintf(stderr, "File not found: %s\n", src);
		return 1;
	}

	if (lseek64(fd_in, curr_offset, SEEK_SET) < 0) {
		fprintf(stderr, "input seek failed\n");
		close(fd_in);
		return 1;
	}

	fd_out = open(tgt, O_WRONLY | O_CREAT, 0777);

	if (lseek64(fd_out, curr_offset, SEEK_SET) < 0) {
		fprintf(stderr, "output seek failed!\n");
		close(fd_in);
		close(fd_out);
		return -1;
	}

	do {
		rv = read(fd_in, buf, 512);

		if (rv != 512) {
			fprintf(stderr, "hmmm, short read: %d?\n", rv);
		}

		if (rv >= 10) {
			if (memcmp(buf + 2, "ASMI-SD", 7) == 0) {
				fprintf(stderr, "found ASMI...\n");
				close(fd_in);
				close(fd_out);
				return 1;
			}
		} else if (rv < 0) {
			fprintf(stderr, "found IO-Error...\n");
			close(fd_in);
			close(fd_out);
			return 1;
		}

		write(fd_out, buf, rv);
		curr_offset += rv;
	} while (rv > 0);

	close(fd_in);
	close(fd_out);

	return 0;
}

int main(int argc, char **argv)
{
	const char *usb_device_file;
	const char *part_file;
	const char *file_on_part;
	const char *mount_point;
	const char *target_file;
	char umount_cmd[1024];
	char mount_cmd[1024];
	int rv;

	if (argc != 7) {
		fprintf(stderr, "Usage: sd_card_recover usb-device-filename partition-file file-on-partition mount-point target-file start-offset\n");
		return 1;
	}

	usb_device_file = argv[1];
	part_file = argv[2];
	file_on_part = argv[3];
	mount_point = argv[4];
	target_file = argv[5];
	curr_offset = atoll(argv[6]);

	snprintf(umount_cmd, sizeof(umount_cmd), "umount %s", part_file);
	snprintf(mount_cmd, sizeof(mount_cmd), "mount %s %s", part_file,
		 mount_point);
	
	system(umount_cmd);

	do {
		if (system(mount_cmd) != 0) {
			fprintf(stderr, "Mount-Cmd failed!\n");
			return 1;
		}

		rv = copy_loop(file_on_part, target_file);

		if (system(umount_cmd) != 0) {
			fprintf(stderr, "Umount-Cmd failed!\n");
			return 1;
		}
		
		usbreset(usb_device_file);

		sleep(20);

	} while (rv == 1);
	
	return 0;
}

