# ************************************************************************
# MyCopy ist eine einfache alternative zu XCopy, das bei vielen Dateien abbricht
#
# Applikation:    MyCopy
# Version:        V0.1.0
# Datum:          09.08.2026
# Author:         SYSTEM-CLINCH Aadorf GmbH, Manuel Magnin
# Support:        Support@Clinch.ch
# Umbebung:       Windows mit Python 3.X Installation
# Lizenz:         Free
# KI / AI:        Code KI Unterstuetzt erstellt
# Anforderung:    Windows 7 oder hoeher mit 1 Kern und midestens 2.0GHz CPU, Last unter 10%
# Leistung:       Bis 80% des langsamsten Geraetes, HDD, USB-Disk, NAS, Netzwerkkarte
# Leistung2:      Auf langsamen PCs ist das Antivirus & der Defenser zu Stoppen
# 
# Zweck:          Zum kopieren von Dateien inkl. Unterverzeichnissen
#                 Kopiert geoeffnete Dateien, uebergeht schreib und lese Fehler
#                 Vorhandene gleichnamige Dateien werden ueberschrieben
#                 Ideal zum Datensichern uf ein NAS oder USB Festplatten
#
# Verwendung:     python MyCopy.py <Quelle> <Ziel>
# Unterstuetzung: Es sind Laufwerksbuchstaben und UNC Pfade moeglich
# Spez.Pafde:     Pfade mit Leerzeichen muessen in "" stehen
#
# Beispiel1:      python MyCopy.py C:\ D:\Backup
# Beispiel2:      python MyCopy.py C:\ D:\
# Beispiel3:      python MyCopy.py C:\ \\Server02\Backup
# Beispiel4:      python MyCopy.py \\Server01\Daten \\Server02\Backup
# Beispiel5:      python MyCopy.py "\\Server 01\Daten" "\\Server 02\Backup"
#
# ************************************************************************


import os
import sys
import shutil
from datetime import datetime

#Globale Variablen
files_copied = 0
files_skipped = 0

def log(message):
    """Ausgabe mit Zeitstempel."""
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"\r[{timestamp}] {message}")

def logline(message):
    """Ausgabe auf einer Zeile mit Zeitstempel."""
    global files_copied
    if len(message) > 65:
        message = "..." + message[-(65-3):]
    print(f"Datei: {files_copied} * {message}          \r",end="",flush=True)

def copy_file(source_file, target_file):
    """Kopiert eine einzelne Datei und ueberschreibt die Zieldatei."""

    global files_copied
    global files_skipped

    try:
        # Pruefen, ob die Quelldatei lesbar ist
        with open(source_file, "rb") as f:
            f.read(1)

    except (OSError, PermissionError) as e:
        log(f"UEBERSPRUNGEN - Datei nicht lesbar: Fehler: {e}   ")
        return False

    try:
        # Zielordner sicherstellen
        target_dir = os.path.dirname(target_file)

        if target_dir:
            os.makedirs(target_dir, exist_ok=True)

        # Datei kopieren und vorhandene Datei ueberschreiben
        shutil.copy2(source_file, target_file)

        return True

    except (OSError, PermissionError) as e:
        log(f"UEBERSPRUNGEN - Datei nicht schreibbar: Fehler: {e}   ")
        return False


def copy_directory(source, target):
    """Kopiert einen kompletten Verzeichnisbaum."""
    global files_copied
    global files_skipped

    log(f"MyCopy  : SYSTEM-CLINCH Support@Clinch.ch")
    log(f"Quelle : {source}")
    log(f"Ziel   : {target}")
    log("")

    # Quelle pruefen
    if not os.path.exists(source):
        log(f"FEHLER: Quelle existiert nicht: {source}")
        return False

    if not os.path.isdir(source):
        log(f"FEHLER: Quelle ist kein Verzeichnis: {source}")
        return False

    # Zielverzeichnis anlegen
    try:
        os.makedirs(target, exist_ok=True)
    except (OSError, PermissionError) as e:
        log(f"FEHLER: {e}  Zielverzeichnis kann nicht erstellt werden: {target}")
        return False

    # Verzeichnisbaum durchsuchen
    for current_dir, directories, files in os.walk(source):

        # Relativen Pfad zur Quelle bestimmen
        relative_dir = os.path.relpath(current_dir, source)

        if relative_dir == ".":
            target_dir = target
        else:
            target_dir = os.path.join(target, relative_dir)

        # Zielordner erstellen
        try:
            if not os.path.exists(target_dir):
                os.makedirs(target_dir)
        except (OSError, PermissionError) as e:
            log(f"UEBERSPRUNGEN - Ordner kann nicht erstellt werden: Fehler: {e}  ")

            # Dateien in diesem Ordner ebenfalls ueberspringen
            files_skipped += len(files)
            continue

        # Dateien kopieren
        for filename in files:

            source_file = os.path.join(current_dir, filename)
            target_file = os.path.join(target_dir, filename)

            logline(f"Kopiere: {source_file}")

            if copy_file(source_file, target_file):
                files_copied += 1
            else:
                files_skipped += 1

    log("")
    log("========================================")
    log("Der Kopiervorgang ist abgeschlossen")
    log("========================================")
    log(f"Dateien kopiert:       {files_copied}")
    log(f"Dateien uebersprungen: {files_skipped}")
    log("")

    return True


def main():

    # Anzahl Parameter pruefen
    if len(sys.argv) != 3:
        print("")
        print("MyCopy - Verzeichnis rekursiv kopieren")
        print("")
        print("Aufruf:")
        print("    MyCopy <Quelle> <Ziel>")
        print("")
        print("Beispiele:")
        print(r"Python  MyCopy C:\ D:\Temp")
        print(r"Python  MyCopy C:\Daten D:\Backup")
        print(r"Python  MyCopy \\Srv1\Daten \\Srv2\Backup")
        print("")
        return 1

    source = sys.argv[1]
    target = sys.argv[2]

    # Absolute Pfade verwenden
    source = os.path.abspath(source)
    target = os.path.abspath(target)

    # Verhindern, dass Ziel innerhalb der Quelle liegt
    source_with_sep = os.path.join(source, "")
    target_with_sep = os.path.join(target, "")

    if target_with_sep.lower().startswith(source_with_sep.lower()):
        log("FEHLER: Das Ziel darf nicht innerhalb der Quelle liegen.")
        log("")
        log(f"Quelle: {source}")
        log(f"Ziel  : {target}")
        return 1

    # Kopiervorgang starten
    success = copy_directory(source, target)

    return 0 if success else 1


if __name__ == "__main__":
    sys.exit(main())
