<template>
  <div id="app" style="position: relative;">
    <main role="main" class="container-fluid">
      <div style="padding-top: 24px; position: relative;">
        <!-- IGV 위 컨트롤 바 -->
        <div style="display: flex; gap: 16px; align-items: center; margin-bottom: 8px; flex-wrap: wrap;">
          <!-- 파란 핸들 위치 input -->
          <label>시작 위치
            <input type="number" :value="startPos" @input="onInputPosChange('start', $event.target.value)" class="igv-control-input" style="width:110px; margin-left:4px;" />
          </label>
          <label>끝 위치
            <input type="number" :value="endPos" @input="onInputPosChange('end', $event.target.value)" class="igv-control-input" style="width:110px; margin-left:4px;" />
          </label>
          <button @click="reverseSequence" :class="['igv-control-btn', { 'reverse-active': isReverse }]" style="margin-bottom: 0; margin-left: 8px;">리버스(상보서열) 변환</button>
          
          <!-- 서버 파일 목록 드롭다운 -->
          <div style="margin-left: 8px; position: relative;">
            <button @click="toggleServerFilesDropdown" class="igv-control-btn">
              서버 파일 목록
              <span v-if="isServerFilesDropdownOpen">▼</span>
              <span v-else>▶</span>
            </button>
            <div v-if="isServerFilesDropdownOpen" class="server-files-dropdown">
              <div v-if="loadingFiles" class="dropdown-item">로딩 중...</div>
              <div v-else-if="serverFiles.length === 0" class="dropdown-item">업로드된 파일이 없습니다.</div>
              <div v-else>
                <div 
                  v-for="file in serverFiles" 
                  :key="file.fileName"
                  @click="selectServerFile(file)"
                  class="dropdown-item file-item"
                  :class="{ 'selected': selectedServerFile && selectedServerFile.fileName === file.fileName }"
                >
                  <div class="file-name">{{ file.fileName }}</div>
                  <div class="file-info">
                    크기: {{ formatFileSize(file.fileSize) }} | 
                    업로드: {{ formatDate(file.uploadTime) }}
                  </div>
                </div>
              </div>
            </div>
          </div>
          
          <!-- FASTA 업로드 -->
          <label style="margin-left: 8px;">
            <input type="file" accept=".fa,.fasta" @change="onFastaUpload" style="display:none;" ref="fastaInput" />
            <button type="button" class="igv-control-btn" @click="$refs.fastaInput.click()">FASTA 업로드</button>
          </label>
          <!-- 대용량 파일 분할 -->
          <button v-if="showSplitOption" @click="splitLargeFile" class="igv-control-btn" style="margin-left:4px;">파일 분할</button>
          <!-- 범위 내 염기서열 변경 -->
          <input type="text" v-model="editSequence" placeholder="새 염기서열 입력" class="igv-control-input" style="width:180px; margin-left:8px;" />
          <button @click="replaceSequenceInRange" class="igv-control-btn" style="margin-left:4px;">범위 염기서열 변경</button>
          <!-- Tracks 드롭다운 -->
          <ul
            class="navbar-nav mr-auto"
            style="list-style:none; padding-left:0; margin:0;"
          >
            <li
              class="nav-item dropdown"
              style="position: relative; display: inline-block;"
            >
              <a
                href="#"
                id="igv-example-api-dropdown"
                @click.prevent="toggleDropdown"
                aria-haspopup="true"
                :aria-expanded="isDropdownOpen.toString()"
                style="color: black; cursor: pointer; user-select: none;"
                >Tracks</a
              >
              <ul
                v-show="isDropdownOpen"
                class="dropdown-menu"
                style="width:350px; position: absolute; top: 100%; left: 0; background: white; border: 1px solid #ccc; box-shadow: 0 2px 5px rgba(0,0,0,0.15); padding: 5px 0; margin: 0; list-style:none; z-index: 1000;"
              >
                <li>
                  <a
                    href="#"
                    @click.prevent="loadCopyNumberTrack"
                    style="display:block; padding: 8px 20px; color: black; text-decoration: none;"
                    >Copy Number</a
                  >
                </li>
                <li>
                  <a
                    href="#"
                    @click.prevent="loadDbSnpTrack"
                    style="display:block; padding: 8px 20px; color: black; text-decoration: none;"
                    >dbSNP 137 (bed tabix)</a
                  >
                </li>
                <li>
                  <a
                    href="#"
                    @click.prevent="loadBigWigTrack"
                    style="display:block; padding: 8px 20px; color: black; text-decoration: none;"
                    >Encode bigwig</a
                  >
                </li>
                <li>
                  <a
                    href="#"
                    @click.prevent="loadBamTrack"
                    style="display:block; padding: 8px 20px; color: black; text-decoration: none;"
                    >1KG Bam (HG02450)</a
                  >
                </li>
              </ul>
            </li>
          </ul>
        </div>
        <!-- IGV 뷰어 영역 -->
        <div id="igvDiv" ref="igvDiv" style="padding-top: 20px; min-height: 500px; position: relative;"></div>
        <!-- 염기서열 결과 -->
        <div style="margin-top: 12px;">
          <strong :class="{ 'reverse-active': isReverse }">염기서열:</strong>
          <pre :class="{ 'reverse-active': isReverse }" style="white-space: pre-wrap; word-break: break-all;">{{ displaySequence }}</pre>
        </div>
      </div>
    </main>
  </div>
</template>

<script>
// igv.js와 igv_custom.js를 script 태그로 동적 로드(SSR 방지)
if (typeof window !== 'undefined') {
  function loadScriptOnce(src, globalCheck, callback) {
    if (globalCheck()) {
      if (callback) callback();
      return;
    }
    
    // 이미 로딩 중인지 확인
    if (document.querySelector(`script[src="${src}"]`)) {
      const checkInterval = setInterval(() => {
        if (globalCheck()) {
          clearInterval(checkInterval);
          if (callback) callback();
        }
      }, 100);
      return;
    }
    
    const script = document.createElement('script');
    script.src = src;
    script.onload = function() { 
      if (callback) callback(); 
    };
    script.onerror = function() {
      console.error(`Failed to load script: ${src}`);
    };
    document.head.appendChild(script);
  }
  
  // igv.js 먼저 로드
  loadScriptOnce('/dist/igv.js', function() { 
    return typeof window.igv !== 'undefined' && typeof window.igv.createBrowser === 'function'; 
  }, function() {
    console.log('IGV.js loaded successfully');
    // igv_custom.js는 igv.js가 로드된 후에만 로드
    loadScriptOnce('/dist/igv_custom.js', function() { 
      return !!window.igvCustomLoaded; 
    }, function() { 
      window.igvCustomLoaded = true; 
      console.log('IGV Custom loaded successfully');
    });
  });
}

export default {
  name: "App",
  data() {
    return {
      browser: null,
      locus: null, // ex: "chr1:10000-10200"
      overlayWidth: 900,
      overlayHeight: 500,
      sequence: '',
      displaySequence: '',
      isReverse: false,
      isDropdownOpen: false,
      startPos: null,
      endPos: null,
      prevStartPos: null,
      prevEndPos: null,
      fastaFile: null, // 업로드된 FASTA 파일 Blob
      fastaName: '', // 업로드된 FASTA 파일명
      editSequence: '', // 범위 내 교체할 염기서열
      showSplitOption: false, // 파일 분할 옵션 표시
      largeFile: null, // 대용량 파일 저장
      isServerFilesDropdownOpen: false, // 서버 파일 목록 드롭다운 상태
      loadingFiles: false, // 서버 파일 목록 로딩 상태
      serverFiles: [], // 서버에 업로드된 파일 목록
      selectedServerFile: null, // 선택된 서버 파일
    };
  },
  watch: {
    locus: 'fetchSequence',
  },
  async mounted() {
    // IGV.js와 igv_custom.js를 script 태그로 동적 로드(SSR 방지)
    if (typeof window !== 'undefined') {
      function loadScriptOnce(src, globalCheck, callback) {
        if (globalCheck()) {
          if (callback) callback();
          return;
        }
        // 이미 로딩 중인지 확인
        if (document.querySelector(`script[src="${src}"]`)) {
          const checkInterval = setInterval(() => {
            if (globalCheck()) {
              clearInterval(checkInterval);
              if (callback) callback();
            }
          }, 100);
          return;
        }
        const script = document.createElement('script');
        script.src = src;
        script.onload = function() { 
          if (callback) callback(); 
        };
        script.onerror = function() {
          console.error(`Failed to load script: ${src}`);
        };
        document.head.appendChild(script);
      }
      // igv.js 먼저 로드
      loadScriptOnce('/dist/igv.js', function() { 
        return typeof window.igv !== 'undefined' && typeof window.igv.createBrowser === 'function'; 
      }, function() {
        console.log('IGV.js loaded successfully');
        // igv_custom.js는 igv.js가 로드된 후에만 로드
        loadScriptOnce('/dist/igv_custom.js', function() { 
          return !!window.igvCustomLoaded; 
        }, function() { 
          window.igvCustomLoaded = true; 
          console.log('IGV Custom loaded successfully');
        });
      });
    }
    await this.initIGV();
    // IGV가 완전히 생성될 때까지 대기
    const registerListener = () => {
      if (window.igvCustomLoaded && this.browser) {
        window.addEventListener('igv-blue-lines-changed', this.onBlueLinesChanged);
      } else {
        setTimeout(registerListener, 200);
      }
    };
    registerListener();
  },
  beforeUnmount() {
    window.removeEventListener('igv-blue-lines-changed', this.onBlueLinesChanged);
  },
  methods: {
    async initIGV() {
      try {
        await this.waitForIGV();
        await this.$nextTick();
        const igvDiv = this.$refs.igvDiv;
        if (!igvDiv) {
          console.error("❌ #igvDiv가 존재하지 않습니다.");
          return;
        }
        // FASTA 업로드 시 genome 옵션 변경
        const genomeOpt = this.fastaFile
          ? {
              id: this.fastaName || 'custom',
              fastaURL: this.fastaFile,
              indexURL: undefined,
            }
          : 'hg19';
        console.log('IGV 옵션:', { locus: this.locus || "chr1:10000-10200", genome: genomeOpt });
        const options = {
          locus: this.locus || "chr1:10000-10200",
          genome: genomeOpt,
        };
        try {
          console.log('IGV 브라우저 생성 시작');
          this.browser = await window.igv.createBrowser(igvDiv, options);
          console.log('IGV 브라우저 생성 완료:', this.browser);
          // 이전 좌표 저장용
          // prevStartPos, prevEndPos는 data에 저장된 this.prevStartPos, this.prevEndPos 사용
          this.browser.on("locuschange", (locus) => {
            // locus 업데이트
            this.locus = locus;
            // locus 파싱
            let locusStart, locusEnd;
            if (typeof locus === 'string') {
              const match = locus.match(/(chr[\w\d]+):(\d+)-(\d+)/);
              if (!match) return;
              locusStart = parseInt(match[2].replace(/,/g, ''), 10);
              locusEnd = parseInt(match[3].replace(/,/g, ''), 10);
            } else if (typeof locus === 'object' && locus.chr && locus.start && locus.end) {
              locusStart = parseInt(String(locus.start).replace(/,/g, ''), 10);
              locusEnd = parseInt(String(locus.end).replace(/,/g, ''), 10);
            } else {
              return;
            }
            // input 박스의 startPos, endPos 기준으로 ratio 계산
            if (this.startPos !== null && this.endPos !== null) {
              const startRatio = (this.startPos - locusStart) / (locusEnd - locusStart);
              const endRatio = (this.endPos - locusStart) / (locusEnd - locusStart);
              if (window.igvCustom && typeof window.igvCustom.setLineRatios === 'function') {
                window.igvCustom.setLineRatios(startRatio, endRatio);
              }
            }
          });
          this.locus = options.locus;
          // IGV, igvCustom 모두 준비된 후 최초 막대/염기서열 동기화
          const syncInitialBlueLines = async () => {
            if (window.igvCustom && typeof window.igvCustom.getLineRatios === 'function') {
              // locus 파싱
              let chrom, locusStart, locusEnd;
              if (typeof this.locus === 'string') {
                const match = this.locus.match(/(chr[\w\d]+):(\d+)-(\d+)/);
                if (!match) return;
                chrom = match[1];
                locusStart = parseInt(match[2].replace(/,/g, ''), 10);
                locusEnd = parseInt(match[3].replace(/,/g, ''), 10);
              } else if (typeof this.locus === 'object' && this.locus.chr && this.locus.start && this.locus.end) {
                chrom = this.locus.chr;
                locusStart = parseInt(String(this.locus.start).replace(/,/g, ''), 10);
                locusEnd = parseInt(String(this.locus.end).replace(/,/g, ''), 10);
              } else {
                return;
              }
              const { startRatio, endRatio } = window.igvCustom.getLineRatios();
              const pos1 = Math.round(locusStart + (locusEnd - locusStart) * startRatio);
              const pos2 = Math.round(locusStart + (locusEnd - locusStart) * endRatio);
              const start = Math.min(pos1, pos2);
              const end = Math.max(pos1, pos2);
              this.startPos = start;
              this.endPos = end;
              if (end - start < 1) {
                this.sequence = '';
                this.displaySequence = '';
                return;
              }
              // 염기서열 fetch
              if (
                this.browser.genome &&
                this.browser.genome.sequence &&
                typeof this.browser.genome.sequence.getSequence === 'function'
              ) {
                const seq = await this.browser.genome.sequence.getSequence(chrom, start, end);
                this.sequence = seq || '';
                this.displaySequence = this.isReverse ? this.getComplement(seq) : seq;
              }
            } else {
              setTimeout(syncInitialBlueLines, 200);
            }
          };
          syncInitialBlueLines();
        } catch (error) {
          console.error("IGV 브라우저 생성 중 오류:", error);
        }
      } catch (error) {
        console.error("IGV 초기화 중 오류:", error);
      }
    },
    // 파란 라인 위치 변경 이벤트 핸들러
    async onBlueLinesChanged(e) {
      if (!this.locus || !this.browser) {
        this.sequence = 'locus/browser 없음';
        this.displaySequence = 'locus/browser 없음';
        this.startPos = null;
        this.endPos = null;
        if (window.igvCustom && typeof window.igvCustom.setMiniMapInfo === 'function') {
          window.igvCustom.setMiniMapInfo(null, null, null);
        }
        return;
      }
      let chrom, locusStart, locusEnd;
      if (typeof this.locus === 'string') {
        const match = this.locus.match(/(chr[\w\d]+):(\d+)-(\d+)/);
        if (!match) {
          this.sequence = 'locus 파싱 실패';
          this.displaySequence = 'locus 파싱 실패';
          this.startPos = null;
          this.endPos = null;
          if (window.igvCustom && typeof window.igvCustom.setMiniMapInfo === 'function') {
            window.igvCustom.setMiniMapInfo(null, null, null);
          }
          return;
        }
        chrom = match[1];
        locusStart = parseInt(match[2].replace(/,/g, ''), 10);
        locusEnd = parseInt(match[3].replace(/,/g, ''), 10);
      } else if (typeof this.locus === 'object' && this.locus.chr && this.locus.start && this.locus.end) {
        chrom = this.locus.chr;
        locusStart = parseInt(String(this.locus.start).replace(/,/g, ''), 10);
        locusEnd = parseInt(String(this.locus.end).replace(/,/g, ''), 10);
      } else {
        this.sequence = 'locus 파싱 실패';
        this.displaySequence = 'locus 파싱 실패';
        this.startPos = null;
        this.endPos = null;
        if (window.igvCustom && typeof window.igvCustom.setMiniMapInfo === 'function') {
          window.igvCustom.setMiniMapInfo(null, null, null);
        }
        return;
      }
      const { startRatio, endRatio } = e.detail;
      const pos1 = Math.round(locusStart + (locusEnd - locusStart) * startRatio);
      const pos2 = Math.round(locusStart + (locusEnd - locusStart) * endRatio);
      const start = Math.min(pos1, pos2);
      const end = Math.max(pos1, pos2);
      this.startPos = start;
      this.endPos = end;
      if (window.igvCustom && typeof window.igvCustom.setMiniMapInfo === 'function') {
        window.igvCustom.setMiniMapInfo(chrom, start, end);
      }
      if (end - start < 1) {
        this.sequence = '';
        this.displaySequence = '';
        return;
      }
      try {
        if (
          this.browser.genome &&
          this.browser.genome.sequence &&
          typeof this.browser.genome.sequence.getSequence === 'function'
        ) {
          const seq = await this.browser.genome.sequence.getSequence(chrom, start, end);
          this.sequence = seq || '';
          this.displaySequence = this.isReverse ? this.getComplement(seq) : seq;
          return;
        }
        this.sequence = 'getSequence 없음(IGV 내부 구조 확인 필요)';
        this.displaySequence = 'getSequence 없음(IGV 내부 구조 확인 필요)';
      } catch {
        this.sequence = '불러오기 실패';
        this.displaySequence = '불러오기 실패';
      }
      // locus 이동 전 좌표 저장
      this.prevStartPos = this.startPos;
      this.prevEndPos = this.endPos;
    },
    reverseSequence() {
      this.isReverse = !this.isReverse;
      this.displaySequence = this.isReverse ? this.getComplement(this.sequence) : this.sequence;
    },
    getComplement(seq) {
      if (!seq) return '';
      return seq.replace(/[ATCG]/gi, c => {
        switch (c.toUpperCase()) {
          case 'A': return 'T';
          case 'T': return 'A';
          case 'C': return 'G';
          case 'G': return 'C';
          default: return c;
        }
      });
    },
    toggleDropdown() {
      this.isDropdownOpen = !this.isDropdownOpen;
    },
    closeDropdown() {
      this.isDropdownOpen = false;
    },
    handleClickOutside(event) {
      const dropdown = this.$el.querySelector("#igv-example-api-dropdown");
      const menu = this.$el.querySelector(".dropdown-menu");
      if (
        dropdown &&
        menu &&
        !dropdown.contains(event.target) &&
        !menu.contains(event.target)
      ) {
        this.closeDropdown();
      }
    },
    waitForIGV() {
      return new Promise((resolve, reject) => {
        let attempts = 0;
        const maxAttempts = 100; // 10초 타임아웃
        
        const checkIGV = () => {
          attempts++;
          
          if (typeof window.igv !== "undefined" && typeof window.igv.createBrowser === "function") {
            console.log('IGV is ready');
            resolve();
          } else if (attempts >= maxAttempts) {
            console.error('IGV loading timeout');
            reject(new Error('IGV loading timeout'));
          } else {
            setTimeout(checkIGV, 100);
          }
        };
        checkIGV();
      });
    },
    loadCopyNumberTrack() {
      if (this.browser) {
        this.browser.loadTrackList = this.browser.loadTrackList.bind(this.browser);
        this.browser.loadTrackList([
          {
            url: "https://s3.amazonaws.com/igv.org.demo/GBM-TP.seg.gz",
            indexed: false,
            isLog: true,
            name: "GBM Copy # (TCGA Broad GDAC)",
          },
        ]);
      }
      this.closeDropdown();
    },
    loadDbSnpTrack() {
      if (this.browser) {
        this.browser.loadTrackList = this.browser.loadTrackList.bind(this.browser);
        this.browser.loadTrackList([
          {
            type: "annotation",
            format: "bed",
            url: "https://data.broadinstitute.org/igvdata/annotations/hg19/dbSnp/snp137.hg19.bed.gz",
            indexURL:
              "https://data.broadinstitute.org/igvdata/annotations/hg19/dbSnp/snp137.hg19.bed.gz.tbi",
            visibilityWindow: 200000,
            name: "dbSNP 137",
          },
        ]);
      }
      this.closeDropdown();
    },
    loadBigWigTrack() {
      if (this.browser) {
        this.browser.loadTrackList = this.browser.loadTrackList.bind(this.browser);
        this.browser.loadTrackList([
          {
            type: "wig",
            format: "bigwig",
            url: "https://s3.amazonaws.com/igv.broadinstitute.org/data/hg19/encode/wgEncodeBroadHistoneGm12878H3k4me3StdSig.bigWig",
            name: "Gm12878H3k4me3",
          },
        ]);
      }
      this.closeDropdown();
    },
    loadBamTrack() {
      if (this.browser) {
        this.browser.loadTrackList = this.browser.loadTrackList.bind(this.browser);
        this.browser.loadTrackList([
          {
            type: "alignment",
            format: "bam",
            url: "https://1000genomes.s3.amazonaws.com/phase3/data/HG02450/alignment/HG02450.mapped.ILLUMINA.bwa.ACB.low_coverage.20120522.bam",
            indexURL:
              "https://1000genomes.s3.amazonaws.com/phase3/data/HG02450/alignment/HG02450.mapped.ILLUMINA.bwa.ACB.low_coverage.20120522.bam.bai",
            name: "HG02450",
          },
        ]);
      }
      this.closeDropdown();
    },
    // input 박스에서 직접 좌표 입력 시 호출
    onInputPosChange(which, val) {
      let locusStart, locusEnd, chrom;
      if (typeof this.locus === 'string') {
        const match = this.locus.match(/(chr[\w\d]+):(\d+)-(\d+)/);
        if (!match) return;
        chrom = match[1];
        locusStart = parseInt(match[2].replace(/,/g, ''), 10);
        locusEnd = parseInt(match[3].replace(/,/g, ''), 10);
      } else if (typeof this.locus === 'object' && this.locus.chr && this.locus.start && this.locus.end) {
        chrom = this.locus.chr;
        locusStart = parseInt(String(this.locus.start).replace(/,/g, ''), 10);
        locusEnd = parseInt(String(this.locus.end).replace(/,/g, ''), 10);
      } else {
        return;
      }
      let start = this.startPos, end = this.endPos;
      if (which === 'start') {
        start = Number(val);
      } else {
        end = Number(val);
      }
      // 범위 보정
      if (start >= end) return;
      // ratio 계산
      const startRatio = (start - locusStart) / (locusEnd - locusStart);
      const endRatio = (end - locusStart) / (locusEnd - locusStart);
      if (window.igvCustom && typeof window.igvCustom.setLineRatios === 'function') {
        window.igvCustom.setLineRatios(startRatio, endRatio);
      }
      if (window.igvCustom && typeof window.igvCustom.setMiniMapInfo === 'function') {
        window.igvCustom.setMiniMapInfo(chrom, start, end);
      }
    },
    // FASTA 파일 업로드 핸들러
    async onFastaUpload(e) {
      const file = e.target.files[0];
      if (!file) return;
      
      console.log('FASTA 파일 업로드:', file.name, file.size);
      this.fastaName = file.name.replace(/\.[^/.]+$/, "");
      // 파란 범위 임시 저장
      const prevStartPos = this.startPos;
      const prevEndPos = this.endPos;
      try {
        // 서버에 파일 업로드
        const formData = new FormData();
        formData.append('file', file);
        
        console.log('서버에 파일 업로드 중...');
        if (typeof window !== 'undefined') {
          const { data: _data, error: _error } = await useApi('/api/fasta/upload', {
            method: 'post',
            body: formData,
            headers: {
              // 'Content-Type'은 브라우저가 자동으로 설정하므로 명시하지 않음
            }
          })
        if (!response.ok) {
          throw new Error(`서버 업로드 실패: ${response.status}`);
        }
        
        
        const result = await response.json();
        console.log('서버 업로드 완료:', result);
        
        // 원격 URL 사용
        this.fastaFile = result.remoteUrl;
        
        // IGV 브라우저 완전 재생성
        if (this.browser) {
          if (typeof this.browser.removeAllTracks === 'function') {
            this.browser.removeAllTracks();
          }
          this.browser = null;
        }
        // igvDiv 완전히 비우기
        const igvDiv = this.$refs.igvDiv;
        if (igvDiv) {
          igvDiv.innerHTML = '';
        }
        // locus, 시퀀스 등 초기화
        this.locus = "chr1:1-100";
        this.sequence = '';
        this.displaySequence = '';
        this.startPos = null;
        this.endPos = null;
        console.log('IGV 재초기화 시작');
        await this.initIGV();
        // IGV 재초기화 후 파란 범위 복원 (딜레이 추가)
        setTimeout(() => {
          if (prevStartPos !== null && prevEndPos !== null && window.igvCustom && typeof window.igvCustom.setLineRatios === 'function') {
            // locus 파싱
            let locusStart = 1, locusEnd = 100;
            if (typeof this.locus === 'string') {
              const match = this.locus.match(/(chr[\w\d]+):(\d+)-(\d+)/);
              if (match) {
                locusStart = parseInt(match[2].replace(/,/g, ''), 10);
                locusEnd = parseInt(match[3].replace(/,/g, ''), 10);
              }
            }
            const startRatio = (prevStartPos - locusStart) / (locusEnd - locusStart);
            const endRatio = (prevEndPos - locusStart) / (locusEnd - locusStart);
            window.igvCustom.setLineRatios(startRatio, endRatio);
            if (typeof window.waitForIGVAndInit === 'function') {
              window.waitForIGVAndInit();
            }
          }
        }, 500);
      }
      } catch (error) {
        console.error('파일 업로드 오류:', error);
        alert(`파일 업로드 실패: ${error.message}\n\n서버 API가 설정되지 않았거나, 파일이 너무 클 수 있습니다.`);
        
        // 서버 업로드 실패 시 기존 방식으로 폴백
        if (file.size <= 100 * 1024 * 1024) { // 100MB 이하만
          this.largeFile = file;
          this.showSplitOption = true;
        }
      }
    },
    // 대용량 파일 분할
    async splitLargeFile() {
      if (!this.largeFile) return;
      
      try {
        const chunkSize = 50 * 1024 * 1024; // 50MB 청크
        const totalChunks = Math.ceil(this.largeFile.size / chunkSize);
        
        alert(`파일을 ${totalChunks}개 청크로 분할합니다. 각 청크는 약 50MB입니다.`);
        
        for (let i = 0; i < totalChunks; i++) {
          const start = i * chunkSize;
          const end = Math.min(start + chunkSize, this.largeFile.size);
          const chunk = this.largeFile.slice(start, end);
          
          // 청크를 파일로 다운로드
          const url = URL.createObjectURL(chunk);
          const a = document.createElement('a');
          a.href = url;
          a.download = `${this.largeFile.name.replace(/\.[^/.]+$/, "")}_part${i + 1}.fasta`;
          document.body.appendChild(a);
          a.click();
          document.body.removeChild(a);
          URL.revokeObjectURL(url);
          
          // 진행률 표시
          const progress = ((i + 1) / totalChunks * 100).toFixed(1);
          console.log(`분할 진행률: ${progress}%`);
        }
        
        alert(`파일 분할 완료! ${totalChunks}개의 파일이 다운로드되었습니다.\n각 파일을 개별적으로 업로드하여 사용하세요.`);
        this.showSplitOption = false;
        this.largeFile = null;
        
      } catch (error) {
        console.error('파일 분할 중 오류:', error);
        alert('파일 분할 중 오류가 발생했습니다.');
      }
    },
    // 범위 내 염기서열 교체
    replaceSequenceInRange() {
      if (!this.sequence || !this.editSequence) {
        alert('염기서열 또는 입력값이 없습니다.');
        return;
      }
      if (this.editSequence.length !== (this.endPos - this.startPos)) {
        alert('입력한 염기서열 길이가 범위와 일치해야 합니다.');
        return;
      }
      // 기존 염기서열을 교체
      // 실제 FASTA 파일은 수정하지 않고, 화면에만 반영
      this.sequence = this.editSequence;
      this.displaySequence = this.isReverse ? this.getComplement(this.sequence) : this.sequence;
      alert('범위 내 염기서열이 변경되었습니다. (화면에만 반영)');
    },
    // 서버 파일 목록 토글
    toggleServerFilesDropdown() {
      this.isServerFilesDropdownOpen = !this.isServerFilesDropdownOpen;
      if (this.isServerFilesDropdownOpen) {
        this.loadServerFiles();
      }
    },
         // 서버 파일 목록 로드
     async loadServerFiles() {
       this.loadingFiles = true;
       try {
         const response = await fetch('http://localhost/api/fasta/files');
         if (!response.ok) {
           throw new Error(`서버 파일 목록 로드 실패: ${response.status}`);
         }
         const result = await response.json();
         this.serverFiles = Array.isArray(result) && Array.isArray(result[0]) ? result[0] : (Array.isArray(result) ? result : []);
       } catch (error) {
         console.error('서버 파일 목록 로드 중 오류:', error);
         this.serverFiles = [];
         alert(`서버 파일 목록을 불러오는데 실패했습니다: ${error.message}`);
       } finally {
         this.loadingFiles = false;
       }
     },
         // 서버 파일 선택
     selectServerFile(file) {
       const prevStartPos = this.startPos;
       const prevEndPos = this.endPos;
       this.selectedServerFile = file;
       this.fastaFile = file.fileUrl; // 선택된 파일의 URL을 fastaFile에 저장
       this.fastaName = file.fileName; // 파일명 업데이트
      
      // IGV 브라우저 완전 재생성
      if (this.browser) {
        if (typeof this.browser.removeAllTracks === 'function') {
          this.browser.removeAllTracks();
        }
        this.browser = null;
      }
      // igvDiv 완전히 비우기
      const igvDiv = this.$refs.igvDiv;
      if (igvDiv) {
        igvDiv.innerHTML = '';
      }
      // locus, 시퀀스 등 초기화
      this.locus = "chr1:1-100";
      this.sequence = '';
      this.displaySequence = '';
      this.startPos = null;
      this.endPos = null;
      console.log('IGV 재초기화 시작');
      this.initIGV().then(() => {
        // IGV 재초기화 후 파란 범위 복원 (딜레이 추가)
        setTimeout(() => {
          if (prevStartPos !== null && prevEndPos !== null && window.igvCustom && typeof window.igvCustom.setLineRatios === 'function') {
            // locus 파싱
            let locusStart = 1, locusEnd = 100;
            if (typeof this.locus === 'string') {
              const match = this.locus.match(/(chr[\w\d]+):(\d+)-(\d+)/);
              if (match) {
                locusStart = parseInt(match[2].replace(/,/g, ''), 10);
                locusEnd = parseInt(match[3].replace(/,/g, ''), 10);
              }
            }
            const startRatio = (prevStartPos - locusStart) / (locusEnd - locusStart);
            const endRatio = (prevEndPos - locusStart) / (locusEnd - locusStart);
            window.igvCustom.setLineRatios(startRatio, endRatio);
            if (typeof window.igvCustom.setupLines === 'function') {
              window.igvCustom.setupLines();
            }
            if (typeof window.waitForIGVAndInit === 'function') {
              window.waitForIGVAndInit();
            }
          }
        }, 500);
      });
      this.isServerFilesDropdownOpen = false; // 드롭다운 닫기
    },
    // 파일 크기 포맷
    formatFileSize(bytes) {
      if (bytes === 0) return '0 Bytes';
      const k = 1024;
      const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    },
    // 날짜 포맷
    formatDate(timestamp) {
      const date = new Date(timestamp);
      return date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
    }
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
}

pre {
  padding: 10px;
  border-radius: 4px;
}

.reverse-active {
  background: #e6f0ff !important;
  color: #0056b3 !important;
  font-weight: bold;
  border: 2px solid #0056b3 !important;
  box-shadow: 0 0 4px #b3d1ff;
}

.igv-control-btn.reverse-active {
  background: #0056b3 !important;
  color: #fff !important;
  border: 2px solid #003366 !important;
}

.nav-tabs {
  margin-bottom: 20px;
}

.tab-content {
  padding-top: 20px;
}

.igv-control-input {
  border: 1.5px solid #007bff;
  padding: 4px 8px;
  border-radius: 5px;
  font-size: 15px;
  outline: none;
  transition: border 0.2s;
}

.igv-control-input:focus {
  border: 2px solid #0056b3;
}

.igv-control-btn {
  border: 1.5px solid #007bff;
  background: #f5faff;
  color: #007bff;
  border-radius: 5px;
  padding: 6px 16px;
  font-size: 15px;
  cursor: pointer;
  transition: background 0.2s, border 0.2s, color 0.2s;
}

.igv-control-btn:hover {
  background: #007bff;
  color: #fff;
  border: 2px solid #0056b3;
}

/* 서버 파일 목록 드롭다운 스타일 */
.server-files-dropdown {
  position: absolute;
  top: 100%;
  left: 0;
  background-color: #fff;
  border: 1px solid #ccc;
  box-shadow: 0 2px 5px rgba(0,0,0,0.15);
  padding: 5px 0;
  margin-top: 5px;
  z-index: 1000;
  max-height: 300px; /* 스크롤 가능하도록 높이 제한 */
  overflow-y: auto;
  border-radius: 4px;
}

.server-files-dropdown .dropdown-item {
  padding: 8px 20px;
  cursor: pointer;
  transition: background-color 0.2s;
}

.server-files-dropdown .dropdown-item:hover {
  background-color: #f0f0f0;
}

.server-files-dropdown .dropdown-item.selected {
  background-color: #e6f0ff;
  font-weight: bold;
  color: #0056b3;
}

.server-files-dropdown .file-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 20px;
  cursor: pointer;
  transition: background-color 0.2s;
}

.server-files-dropdown .file-item:hover {
  background-color: #f0f0f0;
}

.server-files-dropdown .file-item.selected {
  background-color: #e6f0ff;
  font-weight: bold;
  color: #0056b3;
}

.server-files-dropdown .file-name {
  flex-grow: 1;
  margin-right: 10px;
}

.server-files-dropdown .file-info {
  font-size: 0.8em;
  color: #666;
}
</style> 